动态生成DDL时如何防止SQL注入?

How to prevent SQL injection when generating DDL dynamically?

提问人:john 提问时间:1/1/2019 最后编辑:Mark Rotteveeljohn 更新时间:1/2/2019 访问量:1326

问:

目标:动态生成对 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

java jdbc sql 注入

评论

1赞 Elliott Frisch 1/1/2019
没有安全的方法可以做到这一点。
0赞 john 1/1/2019
谢谢你,新年快乐。什么是第二好的?
1赞 Tim Biegeleisen 1/1/2019
第二个版本甚至不起作用。 会起作用,但注射甚至不是最大的问题。您可能不应该允许外部人员创建表。generateSQLBad
1赞 Kevin Anderson 1/1/2019
为什么你认为你首先需要这种能力?必须有更多更可行的替代方案;请帮助我们通过与我们分享您的动机来帮助您选择一个。
1赞 Elliott Frisch 1/1/2019
我想我应该修复它。你应该。仍然没有安全的方法可以做到这一点。不过,我赞同对方的观点,你为什么要这样做?

答:

2赞 Bill Karwin 1/2/2019 #1

不能对标识符(表名和列名)使用参数占位符。它们也不能用于 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 语句或异常。