sql注入漏洞主因是开发人员未意识到代码存在风险且参数化查询使用不当,如动态表名拼接、混用拼接与参数、误用mybatis ${}、信任链断裂、缓存污染、日志注入及静态扫描漏报等。

绝大多数 SQL 注入漏洞不是因为不会修,而是根本没意识到自己正在写漏洞——String query = "SELECT * FROM users WHERE id = " + userId 这类代码在日常 review 中常被当作“能跑就行”,直到渗透测试报告里赫然写着“高危:可盲注获取管理员凭证”。
为什么 PreparedStatement 明明写了却还是被绕过?
参数化查询失效的典型场景,不是没用,而是用错了位置。
- 只对主查询用了
PreparedStatement,但拼接了动态表名或列名(如"SELECT * FROM " + tableName),而这些无法参数化 - 在
WHERE条件中混用拼接和参数,例如:"WHERE status = 'active' AND name LIKE '%" + userInput + "%'"—— 后半段LIKE拼接直接逃逸 - ORM 框架里误用原生 SQL 方法,比如 MyBatis 的
${}(字符串替换)代替#{}(参数绑定),${tableName}就是硬编码式注入入口 - Java 中调用
Statement.execute()执行拼接后的String,哪怕前面声明了PreparedStatement变量也毫无意义
SDL 培训里最容易被跳过的“输入信任链”环节
开发人员默认信任上游数据源,但信任链一环断裂,整条链就失效。
- 从 API 网关透传的
X-User-ID头,未经校验就直插进 SQL —— 实际上该 header 可被客户端伪造 - 缓存层(如 Redis)取回的数据,开发者认为“已清洗过”,结果缓存本身是攻击者通过另一接口注入的
- 后台管理接口允许导出 Excel,导出逻辑读取 URL 参数
?format=csv,但拼接进 SQL 的却是未校验的format值(如用于ORDER BY动态字段) - 日志聚合服务把请求参数写入数据库做审计,但审计表的插入语句用了拼接 —— 攻击者提交
username=admin'--,就能污染审计 SQL 自身
为什么静态扫描工具总漏报?
工具靠模式匹配,而真实漏洞藏在语义里。
- 扫描器能识别
"SELECT * FROM " + table,但识别不出getTableNameByRole(userRole) + "_log"这种间接拼接 - 混淆变量名(如把
sql改成qStr、stmt改成cmd)会让正则规则失效 - SQL 构建分散在多个方法中,拼接发生在 A 方法,执行在 B 方法,跨方法流分析需 IDE 级别支持,普通 CI 插件做不到
- 使用了 JPA 的
@Query(native=true),但内部仍含CONCAT(?, ?)类函数 —— 工具认为“用了 JPA”,实际仍是拼接
真正卡住 SDL 落地的,从来不是“要不要加参数化”,而是“在哪一刻、对哪段值、以什么方式确认它不可信”。一次培训讲十遍 PreparedStatement 不如带学员现场 debug 一条从 Kafka 消费、经 DTO 转换、最终落到 DAO 层的请求链路,亲手标出每个可能被污染的变量节点。











