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);
3条答案
按热度按时间kkih6yb81#
对,只有在使用单个文本值的情况下才能使用prepared statement查询参数。不能将参数用于表名、列名、值列表或任何其他sql语法。
因此,必须将应用程序变量插入到sql字符串中,并适当地引用该字符串。请使用引号来分隔表名标识符,并通过将引号字符串加倍来对其进行转义:
例如,如果表名是
table"name
,您的rdbms标识符引号字符为"
,那么sql
应该包含如下字符串:我也同意@chssply76的评论——最好用户输入的不是文字表名,而是代码Map到表名的表示符,然后插入到sql查询中。这使您能够更加确保不会发生sql注入。
cngwdvgl2#
在这种情况下,您可以通过从databasemetadata获取表列表,根据可用表列表验证表名。实际上,使用正则表达式剥离空格可能更容易,也可能是一些sql保留字“;”,在使用leek string.format构建完整的sql语句之前,先从字符串中提取etc。
不能使用preparedstatement的原因是,它可能将表名封装在“”中,并像字符串一样对其进行转义。
pbgvytdp3#
不可能。你能做的最好的事就是
String#format()
.请注意,这并不能避免sql注入风险。如果
tablename
是用户/客户机控制的值,您需要使用String#replaceAll()
.希望这有帮助。
[编辑]我应该补充:不要将此用于可以使用的列值
PreparedStatement
为了。只需继续使用它的任何列值通常的方式。[edit2]最好不要让用户/客户机按自己的方式输入表名,最好提供一个包含所有有效表名的下拉列表(您可以通过
DatabaseMetaData#getCatalogs()
)以便用户/客户端可以选择它。不要忘记检查服务器端的选择是否有效,因为可能会伪造请求参数。