如何在不使用预准备语句的情况下清理SQL

对于某些sql语句,我不能使用准备好的语句,例如:

SELECT MAX(AGE) FROM ? 

例如,当我想改变表格时。 是否有一个实用程序可以在Java中清理sql? ruby中有一个。

没错,准备好的语句查询参数只能在你使用单个文字值的地方使用。 您不能将参数用于表名,列名,值列表或任何其他SQL语法。

因此,您必须将应用程序变量插入到SQL字符串中并适当地引用字符串。 使用引号来分隔您的表名称标识符,并通过加倍来转义引号字符串:

 java.sql.DatabaseMetaData md = conn.getMetaData(); String q = md.getIdentifierQuoteString(); String sql = "SELECT MAX(AGE) FROM %s%s%s"; sql = String.format(sql, q, tablename.replaceAll(q, q+q), q); 

例如,如果您的表名字面上是table"name ,并且您的RDBMS标识符引用字符是" ,则sql应包含如下字符串:

 SELECT MAX(AGE) FROM "table""name" 

我也同意@ ChssPly76的评论 – 最好是你的用户输入实际上不是文字表名,而是你的代码映射到表名的能指,然后你插入到SQL查询中。 这使您更加确信不会发生SQL注入。

 HashMap h = new HashMap(); /* user-friendly table name maps to actual, ugly table name */ h.put("accounts", "tbl_accounts123"); userTablename = ... /* user input */ if (h.containsKey(userTablename)) { tablename = h.get(userTablename); } else { throw ... /* Exception that user input is invalid */ } String sql = "SELECT MAX(AGE) FROM %s"; /* we know the table names are safe because we wrote them */ sql = String.format(sql, tablename); 

不可能。 最好的方法是使用String#format()

 String sql = "SELECT MAX(AGE) FROM %s"; sql = String.format(sql, tablename); 

请注意,这不会避免SQL注入风险。 如果tablename是用户/客户端控制的值,则需要使用String#replaceAll()对其进行清理。

 tablename = tablename.replaceAll("[^\\w]", ""); 

希望这可以帮助。

[编辑]我应该添加:不要将它用于可以使用PreparedStatement列值。 只需继续使用任何列值的常用方法。

[Edit2]最好不要让用户/客户端能够以他们想要的方式输入表名,但最好在UI中显示包含所有有效表名(可以通过DatabaseMetaData#getCatalogs()的下拉列表,以便用户/客户可以选择它。 如果选择有效,请不要忘记检查服务器端,因为可能会欺骗请求参数。

在这种情况下,您可以通过从DatabaseMetaData获取表列表,根据可用表列表validation表名。 实际上,在使用一些谎言String.format来构建完整的sql语句之前,可能只是更容易使用正则表达式去除空格,也许还有一些sql保留字,“;”等来自字符串。

你不能使用preparedStatement的原因是因为它可能将表名包含在’s中并像字符串一样转义它。