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

Java 如何在不使用预处理语句的情况下清理SQL,java,sql,jdbc,sql-injection,Java,Sql,Jdbc,Sql Injection,对于某些sql语句,我不能使用准备好的语句,例如: SELECT MAX(AGE) FROM ? 例如,当我想改变表格时。是否有一个实用程序可以在Java中清理sql?ruby中有一个。不可能。你能做的最好的事情就是使用它 注意,这并不能避免SQL注入风险。如果tablename是用户/客户端控制的值,则需要使用进行清理 希望这有帮助 [编辑]我应该添加:对于可以使用PreparedStatement的列值,不要使用此选项。只需继续使用任何列值的常规方法即可 [Edit2]最好不要让用户/客户

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

SELECT MAX(AGE) FROM ?

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

不可能。你能做的最好的事情就是使用它

注意,这并不能避免SQL注入风险。如果tablename是用户/客户端控制的值,则需要使用进行清理

希望这有帮助

[编辑]我应该添加:对于可以使用PreparedStatement的列值,不要使用此选项。只需继续使用任何列值的常规方法即可


[Edit2]最好不要让用户/客户机以其想要的方式输入表名,而是最好提供一个包含所有有效表名的下拉列表,您可以通过UI中的DatabaseMetaDatagetCatalogs获得这些表名,以便用户/客户机可以选择它。不要忘记在服务器端检查选择是否有效,因为可能会伪造请求参数。

在这种情况下,您可以通过从数据库元数据获取表列表,根据可用表列表验证表名。实际上,使用正则表达式剥离空格可能更容易,也可能是一些sql保留字;,在使用something liek string.format构建完整的sql语句之前,从字符串中提取etc


无法使用preparedStatement的原因是,它可能将表名封装在s中,并像字符串一样转义它。

对,preparedStatement查询参数只能在使用单个文本值的情况下使用。不能将参数用于表名、列名、值列表或任何其他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);
例如,如果表名为tablename,RDBMS标识符引号字符为,则sql应包含如下字符串:

SELECT MAX(AGE) FROM "table""name"
我也同意@ChssPly76的评论——最好用户输入的不是文字表名,而是代码映射到表名的符号,然后插入到SQL查询中。这为您提供了更多的保证,确保不会发生SQL注入

HashMap h = new HashMap<String,String>();
/* 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); 

@BalusC-+1用于SQL注入引用。如果表名直接来自用户输入,那么您需要担心的问题比清理SQL要大得多,如果没有清理,那么就没有什么可以清理+1用于您对代码映射的注释。这绝对是正确的方向。所有表名都可以通过DatabaseMetaDatagetCatalogs获得,并在UI中表示为下拉列表。但是,如果您在下拉列表中提供真实的表名,则仍应使用映射将用户输入转换为表名,因为输入可能是伪造的。例如,无论下拉列表中显示什么,我都可以在请求参数中键入包含任何内容的URL。使用映射的目的是在收到请求后过滤输入,而不是在输出UI表单之前。
SELECT MAX(AGE) FROM "table""name"
HashMap h = new HashMap<String,String>();
/* 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);