参数化查询是防御时间盲注的唯一铁律,因时序数据库(如Druid)自身无注入检测能力,其SQL解析器不识别SLEEP等函数;动态字段须白名单校验,超时机制仅作兜底。

时序数据库本身不防时间盲注,防御必须落在查询构造和执行层——参数化是铁律,超时是兜底,任何“加索引”“改配置”的思路都绕不开这两条。
Druid 的 SQL 解析链里根本没有时间盲注的检测逻辑
Druid 的 SQLParser 和 Lexer 只做词法/语法解析,用于构建查询计划或审计日志,并不拦截 SLEEP(5)、WAITFOR DELAY 或 BENCHMARK()。它识别 token.sleep 吗?不识别。它的 token 枚举里只有 select、from、where 这类标准关键字,对数据库特有函数完全无感知。
- 你看到 Druid 控制台里报 “SQL parse error”,大概率是语法错误,不是注入拦截
- 如果攻击者用
/*!50000SLEEP(5)*/或SEL%09ECT绕过简单关键词匹配,Druid 依然会正常解析成合法语句并交由引擎执行 - Druid 的防护能力取决于你是否启用了它的
WallFilter(需手动配置),但 WallFilter 默认不启用,且规则仅覆盖基础关键词,对十六进制编码、大小写混淆、嵌套函数等绕过手法基本无效
参数化查询在时序场景下极易被绕过
时序查询常含动态字段:比如 SELECT ${metric} FROM druid.events WHERE __time >= ?。这里的 ${metric} 是字符串拼接,不是参数占位符——? 只能绑定值,不能绑定列名、表名、函数名或 ORDER BY 字段。
- 攻击者传入
metric=cpu_usage, SLEEP(5),拼出来就是SELECT cpu_usage, SLEEP(5) FROM ...,直接触发延时 - MyBatis 的
${}、Jinja2 的{{}}、甚至 Python 的f"SELECT {col} FROM ...",全都会让参数化形同虚设 - 正确做法是白名单校验:
if col not in ["cpu_usage", "mem_used", "disk_io"]: raise ValueError("invalid metric"),而不是尝试“过滤 sleep”
JDBC 驱动层 setQueryTimeout 是唯一可控的止损点
Druid 作为查询服务,其后端连接池(如 HikariCP)和 JDBC 驱动才是执行超时的实际控制者。MySQL 的 MAX_EXECUTION_TIME 对 SELECT 有效,但 Druid 发出的可能是带聚合的 SELECT,也可能是 INSERT INTO ... SELECT,后者完全不受限。
- 必须在 PreparedStatement 上显式调用
setQueryTimeout(3)(单位秒),不能只依赖数据库服务端设置 - PostgreSQL 用户注意:
statement_timeout单位是毫秒,而 JDBC 的setQueryTimeout单位是秒,别设反了 - 超时值要匹配业务 P95:若正常查询 95% 在 800ms 内完成,设 2 秒比设 10 秒更合理——攻击者发 10 个并发
SLEEP(5),10 秒超时等于放行 5 秒的资源占用
真正危险的不是“不知道怎么防”,而是把 WallFilter 当成保险、把白名单漏掉一个字段、或者在 setQueryTimeout 前忘了关掉连接池的 maxWait ——这些细节一旦松动,时间盲注就从理论变成线上事故。


















