只做代码审计或渗透测试均易漏SQL注入,必须双线并行:审计查“能不能拼接”,测试查“拼接后能不能执行”。因审计难发现框架封装、ORM混用原始SQL等隐性拼接,测试则受限于WAF、非标准注入点及错误屏蔽,二者交叉验证才能覆盖全路径。

直接结论:只做代码审计或只做渗透测试,都大概率漏掉SQL注入隐患;必须双线并行,且明确分工——审计查“能不能拼接”,测试查“拼接后能不能执行”。
为什么单靠代码审计会失效
代码审计能发现 mysql_query()、mysqli::query() 这类直连函数的硬编码调用,但对以下情况基本无感:
- 框架封装层(如 Laravel 的
DB::select()传入未绑定的字符串变量) - ORM 链式调用中混入原始 SQL 片段(例如 Django 的
.extra(where=["name = '{}'".format(user_input)])) - 日志埋点、缓存键生成等非显性数据库交互路径里拼接了用户输入
- 第三方 SDK 或内部中间件自动构造 SQL(常见于统一鉴权、审计日志模块)
这类问题在静态扫描中常被标记为“低风险”或直接忽略,但运行时一旦触发,就是高危漏洞。
为什么单靠渗透测试不可信
渗透测试依赖 HTTP 请求响应判断,但很多真实场景下它根本“看不见”注入点:
- 应用启用了 WAF 并拦截了典型 payload(如
' OR '1'='1),但实际后端仍存在拼接逻辑 - 注入点在 Cookie 或自定义 Header 中(如
X-Trace-ID),而测试范围未覆盖这些字段 - 业务逻辑强制校验输入长度/字符集(如手机号字段只允许 11 位数字),导致手工 payload 无法送达数据库
- 错误信息被全局捕获并返回通用提示(如“系统繁忙”),无法通过报错反推闭合方式
换句话说:没测出 ≠ 没漏洞,只是当前测试条件没触达执行路径。
怎么让两者真正互补落地
关键不是“做没做”,而是“怎么做才能交叉验证”:
- 代码审计输出必须带具体行号和上下文片段,例如标出
$sql = "SELECT * FROM orders WHERE uid = " . $_GET['uid'];所在文件及第 42 行,并注明该变量未经过(int)强转或参数化处理 - 渗透测试前,把审计发现的所有疑似拼接点整理成清单,针对性构造绕过 WAF 的 payload(如 URL 编码
%27替代',或用/**/绕过空格过滤) - 对审计认为“已修复”的点,渗透测试必须复测——常见问题是开发者改成了
addslashes(),但 MySQL 开启NO_BACKSLASH_ESCAPES模式时仍可绕过 - 所有测试结果必须回填进代码仓库的 issue 系统,关联到对应 commit,避免“修了又犯”
最易被忽略的一点:审计和测试团队之间不能共享原始凭证(如测试账号、数据库连接串)。否则测试人员可能下意识避开已知高危路径,导致验证失真。

















