Java中的二阶SQL注入问题:为何指定代码分支会引发该漏洞?
先看你的代码片段:
public static boolean jdbcExecute(String query,Statement pstmt, String engineName){ boolean res = false; int dbType = ServerProperty.getReference().getDBType(engineName); PreparedStatement pstmtNew = null; if(pstmt instanceof PreparedStatement){ pstmtNew = (PreparedStatement)pstmt; res = pstmtNew.execute(); // Second order sql injection }else{ res = pstmt.execute(WFSUtil.TO_STRING(query, false, dbType)); // No second order sql injection } return res; }
咱们一步步拆解问题:
先搞懂什么是二阶SQL注入
简单说,二阶注入是恶意数据先被存储到系统(比如数据库),之后在某个业务流程中被取出,拼接/用于构造SQL语句并执行时触发的注入。和普通即时触发的注入不同,它有一个“存储-复用”的延迟过程。
if分支的风险点在哪?
外部传入的PreparedStatement不可控:
这个分支里的pstmt是从方法外部传入的PreparedStatement对象。如果这个对象在创建时,SQL语句是用从数据库/缓存中取出的、之前未被正确过滤的用户输入拼接而成的,风险就埋下来了。举个实际场景:假设之前有用户提交了恶意内容
'); DROP TABLE users;--,这个内容被存到了数据库里。之后业务代码从数据库取出这个内容,直接拼接到了PreparedStatement的SQL模板中(比如SELECT * FROM orders WHERE user_id = '${从库中取出的内容}'),然后把这个带恶意片段的PreparedStatement传到了当前方法里。直接执行未校验的PreparedStatement:
这里直接调用pstmtNew.execute(),没有做任何额外的安全校验或转义处理。当这个PreparedStatement的SQL语句已经包含了之前存储的恶意片段时,执行它就会触发注入——这就是典型的二阶SQL注入场景。
为什么else分支没问题?
else分支里调用了WFSUtil.TO_STRING(query, false, dbType)处理输入的query参数,这个工具方法应该做了SQL特殊字符转义、预编译参数绑定之类的安全处理,把用户输入里的风险字符(比如单引号、分号)转成了数据库能正确识别的普通文本,自然不会触发注入。
内容的提问来源于stack exchange,提问作者Mohd Faizan

