java—如何在不使用准备好的语句的情况下清理sql

sycxhyv7  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(675)

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

SELECT MAX(AGE) FROM ?

例如,当我想改变table时。有没有一个实用程序可以清理java中的sql?有一个是Ruby的。

kkih6yb8

kkih6yb81#

对,只有在使用单个文本值的情况下才能使用prepared statement查询参数。不能将参数用于表名、列名、值列表或任何其他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的评论——最好用户输入的不是文字表名,而是代码Map到表名的表示符,然后插入到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);
cngwdvgl

cngwdvgl2#

在这种情况下,您可以通过从databasemetadata获取表列表,根据可用表列表验证表名。实际上,使用正则表达式剥离空格可能更容易,也可能是一些sql保留字“;”,在使用leek string.format构建完整的sql语句之前,先从字符串中提取etc。
不能使用preparedstatement的原因是,它可能将表名封装在“”中,并像字符串一样对其进行转义。

pbgvytdp

pbgvytdp3#

不可能。你能做的最好的事就是 String#format() .

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

请注意,这并不能避免sql注入风险。如果 tablename 是用户/客户机控制的值,您需要使用 String#replaceAll() .

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

希望这有帮助。
[编辑]我应该补充:不要将此用于可以使用的列值 PreparedStatement 为了。只需继续使用它的任何列值通常的方式。
[edit2]最好不要让用户/客户机按自己的方式输入表名,最好提供一个包含所有有效表名的下拉列表(您可以通过 DatabaseMetaData#getCatalogs() )以便用户/客户端可以选择它。不要忘记检查服务器端的选择是否有效,因为可能会伪造请求参数。

相关问题