SAST能发现SQL注入是因为静态分析代码中危险函数的参数拼接模式,但常报不准,因其无法判断变量是否经intval等过滤或跨函数传递是否净化,对二阶注入、盲注、ORM误用等运行时漏洞则基本无法检测。

为什么SAST能发现SQL注入,但常报不准
SAST工具(如RIPS、Checkmarx)通过静态扫描源码识别危险模式,比如mysql_query、mysqli::query、execute等函数的参数直接拼接用户输入。它不运行程序,只分析代码路径——所以能发现未执行到的分支里潜在的注入点。但问题在于:它无法判断变量是否经过过滤或转义,容易把htmlspecialchars之后再拼接的代码也标为高危。
- 典型误报场景:
$sql = "SELECT * FROM users WHERE id = " . intval($_GET['id']);→ SAST可能仍报警,因未识别intval的净化作用 - 典型漏报场景:使用PDO预处理但绑定参数写错,如
$stmt->bindParam(':id', $_GET['id'], PDO::PARAM_STR)→ 类型设为PARAM_STR却传入数字,SAST通常不校验绑定类型一致性 - PHP中
extract($_GET)+mysql_query("...$var...")这类组合,SAST识别率高;但若变量经多层函数传递(如get_input()→clean()→build_sql()),控制流分析失败就会漏掉
用RIPS扫描PHP项目时的关键配置项
RIPS是目前对PHP SQL注入识别最细的开源SAST之一,但默认配置容易过严或过松。启动前必须调整三个核心参数:
-
-t(target):指定入口文件,如index.php,避免扫描vendor或测试目录引入噪声 -
--filter:排除已知安全函数,例如添加mysql_real_escape_string|mysqli_real_escape_string|htmlspecialchars到白名单,减少误报 -
--minscore:设为5以上,过滤低置信度结果;RIPS对echo $_GET['x']这种明显XSS会打高分,但对深层拼接可能只给2–3分
命令示例:php ripstool.php -t ./web/index.php --filter "mysqli_real_escape_string|htmlspecialchars" --minscore 5
哪些SQL注入类型SAST根本检测不到
SAST对运行时行为无感知,以下几类漏洞它基本无能为力:
- 二阶SQL注入:用户输入先存入数据库,后续读取后拼接进新查询(如评论内容被后台导出功能二次使用)——SAST看不到“存储→读取→拼接”这条跨请求链路
- 时间盲注/布尔盲注逻辑:攻击不依赖报错或回显,只靠响应时间或页面差异,SAST无法模拟HTTP往返,自然无法建模
-
ORM误用:如Django的
extra(where=["username = '%s'" % request.GET['u']]),虽用ORM但手动字符串拼接,部分SAST不解析框架特定API调用 -
堆叠注入:
mysqli_multi_query执行多语句,SAST难以判定第二条语句是否可控,尤其当;来自拆分后的变量拼接
别只信SAST报告,必须人工验证这三处
一份SAST报告里真正要跟进的,往往不到10%。重点盯住:
- 所有涉及
$_GET、$_POST、$_COOKIE、$_SERVER的SQL拼接点,检查是否真没过滤——别轻信注释里的// 已过滤 - 数据库连接初始化位置,确认是否全局禁用了
magic_quotes_gpc(PHP旧版特性,现默认关闭,但遗留代码可能还依赖它) - 是否存在
eval、create_function或call_user_func动态调用SQL相关函数——这类间接执行路径SAST几乎无法追踪
最常被忽略的是日志记录环节:把$_SERVER['QUERY_STRING']原样写入数据库日志表,再在后台管理页用SELECT查出并echo,这就构成隐蔽的二阶注入面,SAST扫不到,但手工审计一眼就能揪出来。











