proxy层timeout对time-based blind sqli无效,因其不介入sql执行过程,无法感知sleep()等延迟函数的线程阻塞;真正有效的拦截需依赖驱动层setquerytimeout或mysql服务端max_execution_time(仅select生效),中间件仅可辅助限流与特征阻断。

数据库中间件无法可靠拦截基于时间的盲注语句,它既不理解业务语义,也看不到 SQL 执行时的真实耗时——你看到的“拦截”,其实是误报或漏报后的侥幸。
为什么 Proxy 层 timeout 设置对 Time-Based Blind SQLi 几乎无效
MySQL Proxy、ShardingSphere、MaxScale 等中间件通常只在连接建立、路由转发、结果返回阶段起作用,但它们不介入 SQL 实际执行过程。一个 SLEEP(10) 语句发到后端 MySQL 后,中间件早已把请求转走,根本不知道线程卡在哪儿。
- 中间件配置的
query_timeout或max_execution_time(如果支持)往往只作用于自身协议解析阶段,不是数据库执行层的超时 - 多数中间件默认不开启语句级耗时监控,即使开了,也是统计平均值或 P95,无法识别单次恶意
BENCHMARK(1000000, ENCODE('a','b'))的突刺 - 攻击者用
SELECT /*+ MAX_EXECUTION_TIME(5000) */ ...这类 hint 可绕过中间件的全局策略,而中间件一般不会重写或拒绝合法 hint
真正能打断 SLEEP() 的只有驱动层 setQueryTimeout 和服务端 max_execution_time
必须分清楚:谁在执行?谁在等待?谁有中断权?
-
PreparedStatement.setQueryTimeout(3)(JDBC)或cursor.execute(sql, timeout=3)(psycopg2)——这是客户端驱动发起的 cancel request,能真正中止正在执行的查询线程 -
SET SESSION max_execution_time = 2000或SELECT /*+ MAX_EXECUTION_TIME(2000) */ ...——这是 MySQL 5.7.8+ 服务端原生机制,仅对SELECT生效,且依赖optimizer_switch中未关闭max_execution_time -
statement_timeout(PostgreSQL)对所有语句生效,但若客户端已断开(如 Nginx proxy_read_timeout 触发),它仍会跑满设定时间,浪费资源
中间件唯一可行的补救角色:限流 + 特征阻断,而非超时拦截
与其指望它“掐断慢查询”,不如让它做两件事:识别可疑模式、压住并发节奏。
- 在 SQL 解析层匹配正则:
(?i)sleep\s*\(|benchmark\s*\(|waitfor\s+delay,命中即拦截或标记为高危会话 - 对同一 IP / user_id 在 60 秒内触发 ≥3 次含
substr+ascii+ 数字比较的查询,自动限流(如每秒 ≤1 次) - 禁用非白名单函数:通过中间件规则重写或拒绝含
if(、case when嵌套SLEEP的语句,这类结构在正常业务中极少出现
最常被忽略的一点:中间件本身没有权限回收数据库函数(如 REVOKE EXECUTE ON FUNCTION sleep FROM 'app'@'%'),也不强制校验参数类型。只要 SQL 能过语法解析,它就放行——而盲注攻击恰恰靠的就是“语法完全合法”。防御重心必须落在 PreparedStatement 预编译、输入类型强校验、以及驱动层 timeout 的硬性设值上,中间件只是辅助哨兵,不是闸门。











