防御基于时间差的盲注攻击最有效手段是监控响应时间突增并拦截,因其不触发错误、不改变状态码,仅通过sleep等函数拖慢响应,服务器返回200 ok,日志中无异常痕迹。

基于时间的盲注攻击本身不触发错误、不改变响应状态码,服务器返回 200 OK,日志里只有一行“正常”请求——它根本不是异常,所以常规日志监控和错误告警完全失效。
为什么 Nginx 或应用日志里看不到盲注痕迹
盲注探测请求(如 id=1 AND IF(1=1,SLEEP(1),0))在服务端执行后,只要数据库没报错,整个 HTTP 生命周期就是合法的:路由匹配、中间件执行、SQL 执行、模板渲染、HTTP 返回。access_log 中只记录 200、1.2s、GET /user?id=1... —— 和一次慢查询毫无区别。
常见误判点:
- 只查 error_log:盲注成功时几乎从不报错
- 只盯 500 状态码:盲注全程走 200
- 用字符串关键词过滤 access_log(如
SLEEP、WAITFOR):攻击者早用大小写混淆、URL 编码、嵌套函数绕过了
数据库端设防必须切断执行路径,而非检测 payload
靠数据库日志或审计插件去“识别 SLEEP()”是徒劳的——它出现在 SQL 字符串里,但真正起作用的是数据库引擎是否允许执行该函数。防御重心应放在权限与执行环境上:
- MySQL:对应用账号显式执行
REVOKE EXECUTE ON PROCEDURE *.*,并确认未启用sys_eval等扩展函数 - PostgreSQL:普通用户默认可调
pg_sleep(),需通过SECURITY DEFINER函数封装查询逻辑,把延迟函数调用权限收归可信函数内部 - SQL Server:禁用
sp_execute_external_script,限制WAITFOR使用范围,生产环境关闭xp_cmdshell - 所有数据库:连接池配置中强制使用最小权限账号,禁止应用以
postgres、sa、root身份直连
参数化查询在数据库层的实际生效位置
很多人以为“用了 PDO::prepare 就安全”,但漏洞常发生在你没意识到的地方:
-
SELECT * FROM t WHERE id = ?安全;但SELECT * FROM ?(表名参数化)不安全——预编译只绑定值,不绑定标识符 - Django 的
raw()、Laravel 的DB::select()、MyBatis 的${}占位符,都跳过预编译,直接拼接进 SQL 字符串 - 存储过程中用
EXEC(@sql)或CONCAT()拼接动态 SQL,等于在数据库内重建注入入口,应用层参数化完全无效 - ORM 的
filter(id__exact=user_input)是安全的,但extra(where=["id = " + user_input])是高危的
最易被忽略的一点:延时函数是否执行,取决于数据库账号权限和 SQL 解析阶段是否已将 payload 当作代码——而这两者,只有在参数化彻底贯彻到每一处动态拼接点时,才能真正阻断。任何一处漏网的字符串拼接,都可能让 SLEEP(1) 在数据库里安静地执行完再返回。










