sqlmap可快速验证基础防护是否形同虚设:轻量扫描(--batch --level=3 --risk=2)看是否提示“Parameter 'uid' is vulnerable”,结合手动测报错、延时及日志分析确认是否真用参数化查询。

用 sqlmap 快速验证基础防护是否形同虚设
很多团队加了参数绑定、输入过滤就以为高枕无忧,结果 sqlmap -u "http://x.com/api?uid=1" 一跑,照样回显数据库名。不是工具太强,是防御没打在关键路径上。
实操建议:
- 先用
--batch --level=3 --risk=2跑轻量扫描,避免触发 WAF 频率封禁 - 重点看
[INFO] testing for SQL injection on GET parameter 'uid'后是否出现Parameter 'uid' is vulnerable - 如果返回
no parameter found,大概率是 URL 解析层就丢了参数(比如 Nginx 重写规则吃掉了 query string) - 别只测
id=1,补上id=1' OR '1'='1和id=1 AND SLEEP(3)手动验一验响应延迟和报错细节
绕过常见“伪防御”的典型 payload 组合
所谓“过滤单引号”“替换 union”只是障眼法,真实渗透中这些根本挡不住。
常见失效点:
- 前端 JS 过滤但后端没校验 → 直接用 curl 绕过,
curl "http://x.com/search?q=%27%20OR%201=1--" - 服务端用
str_replace("union", "", $input)→ 叠加大小写或注释:UnIoN/**/SELECT - WAF 拦了
sleep()但放行BENCHMARK(1000000,MD5(1))→ 时间盲注依然成立 - 参数被转义但拼接进
ORDER BY或GROUP BY子句 → 这里不走预编译,order=1,updatexml(1,concat(0x3a,(SELECT user())),1)照常报错回显
确认是否真用了参数化查询的硬核检查法
光看代码里有没有 prepare() 或 ? 占位符没用,得看执行时到底传了什么。
验证步骤:
- 在数据库侧开慢日志或通用日志(MySQL:
SET GLOBAL general_log = 'ON'),查日志里最终执行的 SQL 是SELECT * FROM user WHERE id = '1'还是SELECT * FROM user WHERE id = 1—— 字符串引号存在,说明变量还是拼进去的 - 对 Java 应用,用 Arthas 拦截
PreparedStatement.execute(),打印toString()结果,看是否含用户输入原文 - Node.js 的
pg或mysql2,检查是否用了pool.query("SELECT * FROM t WHERE id = $1", [req.query.id])这类带参数数组的调用,而非pool.query("SELECT * FROM t WHERE id = " + req.query.id)
盲注场景下如何判断防御是否生效
当页面不报错、不回显、也不跳转,只能靠响应时间或布尔逻辑判断时,防御有效性最易被误判。
关键动作:
- 发两个请求:
id=1 AND 1=1和id=1 AND 1=2,对比 HTTP 状态码、响应体长度、响应头Content-Length是否有稳定差异 - 用
AND IF(1=1,SLEEP(2),0)测延时,注意要排除网络抖动——连续测 5 次,至少 4 次响应 >1800ms 才算有效盲注链路 - 如果所有布尔/延时 payload 都返回一致响应,不代表安全,可能是 WAF 主动拦截并返回固定 403,此时应检查 WAF 日志里是否有
SQLi pattern matched类记录
真正难测的是上下文敏感的注入点,比如插入到 JSON 字段值里、拼进 LIKE 模糊查询的通配符前后、或者作为 LIMIT 的偏移量——这些地方连 sqlmap 默认策略都容易漏,得人工构造边界 case。

















