目标:动态生成对SQL注入的PreparedStatement免疫。
// This is a bad method. SQL injection danger . But it works
private PreparedStatement generateSQLBad(Connection connection, String tableName,
String columnName, String columnType) throws SQLException {
String sql = "create table " + tableName + " (" + columnName + " " + columnType + ")";
PreparedStatement create = connection.prepareStatement(sql);
return create;
}
// I tried this. But it didn't work
private PreparedStatement generateSQLGood(Connection connection, String tableName,
String columnName, String columnType) throws SQLException {
String sql = "create table ? (? ?)";
PreparedStatement create = connection.prepareStatement(sql);
create.setString(1, tableName);
create.setString(2, columnName);
create.setString(3, columnType);
return create;
} 如何动态生成PreparedStatement,用户可以选择表名、列类型等而不存在注入的危险?
发布于 2019-01-01 19:40:46
不能将?参数占位符用于标识符(表名和列名)。它们也不能用于SQL关键字,比如数据类型。准备查询需要能够验证语法,并验证您的表名等等是否合法。这必须在准备时间完成,而不是在执行时间。SQL不允许参数包含语法。它们总是被视为标量值。这就是它们如何防止SQL注入的方式。
因此,参数只能用于替换标量文本,例如引号字符串或日期,或数字值。
如何处理动态标识符?如注释所示,最好的方法是过滤输入,这样它们就不会引入SQL注入。在某种程度上,部分基于用户输入的动态SQL是 SQL注入。你只需要以一种控制的方式允许它。
如果分隔标识符,所有SQL实现都允许在表名中使用特殊字符。标准SQL对分隔符使用双引号.MySQL使用倒计时,使用方括号。
关键是,您可以用这种方式制作奇怪的表名,比如包含空格、标点符号、国际字符或SQL保留词的表名。
CREATE TABLE "my table" ( col1 VARCHAR(20) );
CREATE TABLE "order" ( col1 VARCHAR(20) );也见我对https://stackoverflow.com/a/214344/20860的回答
但是,如果表名本身包含文字双引号呢?那你必须逃离那个角色。要么使用双字符,要么使用反斜杠:
CREATE TABLE "Dwayne ""The Rock"" Johnson" ( col1 VARCHAR(20) );
CREATE TABLE "Dwayne \"The Rock\" Johnson" ( col1 VARCHAR(20) );您也可以设计函数来检查这些字符的动态表名,或者删除它们,或者抛出异常。
但是,即使通过仔细过滤输入使语句安全,这也不可能满足checkmarx警告。SQL注入测试人员无法分析您的自定义代码,以确保它可靠地过滤输入。
您可能只需尽最大努力使动态SQL安全,因为您知道checkmarx总是会抱怨它。在代码中编写注释,向阅读代码的未来开发人员解释您的安全措施。
还编写单元测试,以确保危险的输入导致安全的DDL语句或其他异常。
https://stackoverflow.com/questions/53992860
复制相似问题