盲注检测是攻击入口而非防御手段,防御必须前置到参数预编译与类型校验,禁用动态拼接、关闭错误回显、统一响应状态、杜绝时序与布尔差异。

盲注检测本身不是防御手段,而是攻击入口
发现应用存在 sleep() 或 if(1=1, sleep(2), 0) 可被触发,说明后端已暴露盲注面——这不是“能测出漏洞”,而是“漏洞已被利用”。防御必须前置到参数处理环节,而非靠混淆响应时间或布尔逻辑来干扰探测。
常见错误现象:SELECT * FROM users WHERE id = ? 中用字符串拼接 id,且未校验类型;数据库报错被静默吞掉,导致无报错反馈,攻击者转向盲注。
- 所有用户输入进 SQL 查询前,必须走预编译(
PreparedStatement/pg_query_params/sqlite3_prepare_v2),不拼接、不格式化 - 若必须动态构造表名/列名(极少见),需白名单校验,如
['users', 'orders'],禁止任何通配或正则绕过 - 关闭数据库错误回显(
display_errors=Offin PHP;show_errors=falsein PostgreSQL),但别依赖它防盲注——延迟和布尔差异仍可被量化
time-based 盲注的响应延迟不可靠,别试图“加随机延时”混淆
给每个请求加 usleep(rand(10000, 50000)) 看似打乱节奏,实则无效:攻击者只需提高请求次数(如 50–100 次)做统计去噪,标准差会自然收敛。真正有效的是让「有逻辑分支」的 SQL 不产生可观测的时序差异。
使用场景:旧系统无法改 SQL 架构,只能从响应层临时缓解。
- 禁用
SLEEP()、BENCHMARK()、WAITFOR DELAY等函数(MySQL/SQL Server 配置disabled_functions或权限回收) - 避免在 WHERE 条件中写
IF(@a:=1, SLEEP(2), 0)类表达式——这类逻辑本就不该出现在查询里 - Web 层统一设置超时(如 Nginx
proxy_read_timeout 3s),切断长延迟响应,但注意:这治标不治本,仅防自动化扫描器暴力推断
布尔型盲注靠真假响应差异,而你返回的 200/404/空 JSON 正是它的信号源
当 id=1 AND 1=1 返回用户数据,id=1 AND 1=2 返回空数组或 404,攻击者立刻获得布尔信道。防御关键不是藏状态码,而是让“真”和“假”在 HTTP 层不可区分。
参数差异:后端若对不存在的 id 返回 404,对存在的返回 200 + 数据,就等于主动提供布尔判据。
- 统一返回 200 + 标准结构体(如
{"data": null, "code": 0, "msg": "success"}),业务逻辑错误走code字段,不靠 HTTP 状态码区分 - 禁止根据查询结果数量决定是否抛异常(如
findOneOrFail()在 Laravel 中默认 404)——应改为findOne()+ 显式判空 - 前端不依赖 HTTP 状态码做 UI 分支(比如 404 就显示“找不到”,200 就渲染详情),否则等于把服务端布尔逻辑透传给攻击者
ORM 和 Query Builder 并不自动免疫盲注,看它生成的 SQL 是什么
用 where('id', request('id')) 看似安全,但如果 request('id') 是字符串 "1 OR SLEEP(2)",且 ORM 未强制类型转换,某些老版本 Laravel/Eloquent 或 ThinkPHP 会原样拼入 SQL —— 尤其当字段类型是字符串却用于数字比较时。
性能影响:预编译本身无性能损耗,但滥用 raw()、DB::select(DB::raw(...)) 会绕过参数绑定,直接打开盲注大门。
- 检查 ORM 日志,确认最终执行的 SQL 是否含字面量(如
WHERE id = '1 OR SLEEP(2)'),而不是WHERE id = ? - 对数字型参数,显式转整型:
(int) request('id')或filter_var($id, FILTER_VALIDATE_INT),再喂给查询构建器 - 禁用所有
raw()、expression()、DB::unprepared(),除非你逐行审计过拼接逻辑且确认无用户输入参与
最易被忽略的点:JSON 字段查询(如 MySQL 的 json_extract())、全文检索(MATCH ... AGAINST)、GIS 函数(ST_Contains)——这些场景下,ORM 往往不走标准绑定,极易漏防。










