提问人:john 提问时间:1/1/2019 最后编辑:Mark Rotteveeljohn 更新时间:1/2/2019 访问量:1326
动态生成DDL时如何防止SQL注入?
How to prevent SQL injection when generating DDL dynamically?
问:
目标:动态生成对 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;
}
如何动态生成用户可以选择表名、列类型等的地方,并且没有SQL注入的危险?PreparedStatement
答:
不能对标识符(表名和列名)使用参数占位符。它们也不能用于 SQL 关键字,例如数据类型。准备查询需要能够验证语法,并验证表名等是否合法。这必须在准备时完成,而不是在执行时完成。SQL 不允许参数包含语法。它们始终被视为标量值。这就是他们防止 SQL 注入的方式。?
因此,参数只能用于代替标量文字,例如带引号的字符串或日期或数值。
如何处理动态标识符?正如评论所建议的那样,你能做的最好的事情就是过滤输入,这样它们就不会引入SQL注入。在某种程度上,部分基于用户输入的动态 SQL 是 SQL 注入。你只需要以一种可控的方式允许它。
所有 SQL 实现都允许您在表名中使用特殊字符(如果分隔标识符)。标准 SQL 使用双引号作为分隔符。MySQL使用反引号,Microsoft SQL Server使用方括号。
关键是,您可以以这种方式创建看起来很奇怪的表名,例如包含空格、标点符号、国际字符或 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 语句或异常。
评论
generateSQLBad