• jdbc不能使用占位符代表表名及其解决方法


    jdbc不能使用占位符代表表名

    jdbc执行sql时面临使用statement还是prepareStatement,其中statement写的简单不过会有sql注入最后安全检查不过。prepareStatement就不会,因为使用了占位符?,像myBatis也是占位符,就不会有sql注入。
    不过?只能替换值,不能替换表名。比如select * from ? where id = ‘1’, 执行prepareStatement结果是不对的。诸多原因,编译上的,还有?其实两边默认有单引号。那如何prepareStatement使用占位符替换表名呢?
    答案简单的说,没有!

    不要试图用啥单引号替换,string拼接啥的,要么做不到要么无法避免sql注入。
    结论就是只能用白名单,检查表名
    解决方法:
    您唯一安全的方法是验证用户输入。不过,最安全的方法不是验证它并允许用户输入通过数据库,因为从安全的角度来看,您总是可以指望用户比您的验证更聪明。永远不要相信动态的、用户生成的字符串,连接在你的语句中。
    那么什么是安全验证模式?
    模式 1:预构建安全查询
    1)在代码中一劳永逸地创建所有有效语句。

    Map statementByTableName = new HashMap<>();
    statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
    statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
    
    • 1
    • 2
    • 3

    如果需要,这个创作本身可以通过select * from ALL_TABLES;声明来动态化。ALL_TABLES将返回您的 SQL 用户有权访问的所有表,您还可以从中获取表名和模式名称。
    2)选择Map里面的语句

    String unsafeUserContent = ...
    String safeStatement = statementByTableName.get(usafeUserContent);
    conn.prepareStatement(safeStatement, name);
    
    • 1
    • 2
    • 3

    看看unsafeUserContent变量如何永远不会到达数据库。
    3)制定某种策略或单元测试,以检查您的所有内容statementByTableName是否对您的模式有效,以供其未来发展,并且没有丢失任何表。
    模式:双重检查
    您可以 1) 验证用户输入确实是一个表名,使用无注入查询(我在这里输入伪 sql 代码,您必须对其进行调整以使其正常工作,因为我没有实际检查的 Oracle 实例有用) :

    select * FROM 
        (select schema_name || '.' || table_name as fullName FROM all_tables)
    WHERE fullName = ?
    
    • 1
    • 2
    • 3

    并在此处将您的 fullName 绑定为准备好的语句变量。如果您有结果,那么它是一个有效的表名。然后,您可以使用此结果来构建安全查询。
    模式 3
    它是 1 和 2 之间的混合。您创建一个名为“TABLES_ALLOWED_FOR_DELETION”的表,然后用所有适合删除的表静态填充它。
    然后你让你的验证步骤成为
    conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
    如果这有结果,则执行 safe_table_name。为了更加安全,标准应用程序用户不应写入此表。
    我不知何故觉得第一种模式更好。
    您可以通过使用正则表达式检查表名来避免攻击:

    if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
        final PreparedStatement stmt = connection
                    .prepareStatement("delete from " + fullTableName
                        + " where name= ?");
        stmt.setString(1, addressName);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用这样一组受限制的字符来注入 SQL 是不可能的。

    此外,我们可以从表名中转义任何引号,并将其安全地添加到我们的查询中:

    fullTableName = StringEscapeUtils.escapeSql(fullTableName);
    final PreparedStatement stmt = connection
                .prepareStatement("delete from " + fullTableName
                    + " where name= ?");
    stmt.setString(1, addressName);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    StringEscapeUtils 带有 Apache 的 commons-lang 库。

  • 相关阅读:
    每日三题 8.22
    求解仿射变换矩阵
    sql注入的其他注入
    零售数据分析报表这样做,老板狂点赞!
    使用Python实现3D曲线拟合
    完美世界服务端设置小技巧
    数据结构——线性表:栈、队列
    Taurus.MVC 微服务框架 入门开发教程:项目集成:2、客户端:ASP.NET Core(C#)项目集成:应用中心。
    【MySql】mysql之事务和存储引擎
    本机idea连接虚拟机中的Hbase
  • 原文地址:https://blog.csdn.net/Artisan_w/article/details/126359954