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

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

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

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

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

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

String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);

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

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

并在此处将您的 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);
}

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

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

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

StringEscapeUtils 带有 Apache 的 commons-lang 库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值