结论:二阶注入指恶意输入第一次写入时被正确转义/参数化安全入库,之后另一段代码把这条"已安全"的数据读出来拼进新 SQL 才触发注入。它绕过了"入库处统一参数化"的直觉防线——漏洞在读取后的使用处

展开:经典场景:用户注册名为 admin'--(参数化安全写入);管理员后台有功能UPDATE users SET role='x' WHERE name='" + 读取出的name + "'——开发者误以为"库里取出来的数据是可信的",直接拼接,注入触发。防御本质是统一认识:参数化不是入库一次的仪式,而是每次构造 SQL 的要求——无论数据来自请求、数据库、文件还是消息队列。配套措施:1)ORM 的原始 SQL 接口(raw query)是重灾区,code review 重点盯;2)数据库账户最小权限(应用账号无 DROP/无跨库权限)限制爆炸半径;3)WAF/RASP 做异常检测兜底;4)静态扫描(Semgrep 规则查字符串拼接 SQL)进 CI。易错点:LIKE 通配符、表名/列名/ORDER BY 字段这类无法参数化的位置,必须用白名单映射而非转义。

// 漏洞点:name 来自数据库,被当成可信数据
String name = rs.getString("name");
stmt.execute("UPDATE logs SET cnt=1 WHERE user='" + name + "'");
// 修复:照样参数化
PreparedStatement ps = conn.prepareStatement("UPDATE logs SET cnt=1 WHERE user=?");
ps.setString(1, name);

追问方向:ORM 注入(Hibernate HQL 拼接、Django extra())、存储过程为何不等于天然免疫(内部动态 EXEC 照样可注入)。