数据库代理中间件(如proxysql)在mysql协议解析完成后介入,基于语义级ast节点进行精准过滤,支持digest匹配、上下文感知策略及sql审计,是应用层无法替代的最后一道防线。

SQL注入过滤必须发生在协议解析之后
应用层过滤本质是“猜字符”,而数据库代理中间件(如 ProxySQL、MySQL Audit Plugin)是在 MySQL 协议解析完成、SQL 语义已明确的阶段介入。此时 SELECT、WHERE、ORDER BY 等结构已被识别,参数与逻辑分离清晰,规则匹配不再依赖正则或字符串替换。
比如攻击者用 SEL%00ECT 或 /*comment*/UNION 绕过应用层关键词检查,代理层看到的是解析后的 AST 节点,UNION 就是 UNION,不拼写、不编码、不混淆。
ProxySQL 的 query rule 是语义级拦截点
ProxySQL 允许你基于 digest(标准化 SQL 摘要)或 match_pattern(正则仅用于已解析字段)配置规则,且规则生效位置在 mysql_query_processor 阶段 —— 即 SQL 已被 parser 分词、语法校验通过、但尚未发送到后端节点。
- 可直接拒绝
digest匹配0x1a2b3c...(某类高危慢查询模板)的请求 - 能对
ORDER BY后的列名做白名单校验,因为此时已知该 token 属于sort_item类型 - 支持重写
IN (?, ?, ?)动态占位符,而非硬编码固定个数 —— 这在应用层很难统一控制
应用层做不到的上下文感知
代理知道当前连接是否在事务中、是否刚执行过 SET SESSION sql_mode=...、甚至是否来自某个特定应用标签(通过 client_host 或自定义 user_variable)。这些信息在应用层早已丢失,或需额外埋点传递。
例如:只对来自 reporting_app 用户的 SELECT 请求启用 max_execution_time=5000 限流,同时放行 admin_app 的长查询 —— 这种策略在代理配置里一行搞定,在应用层得改 N 个 DAO 方法。
别把 ORM 的预处理当万能解药
ORM 如 Django ORM 或 MyBatis 默认开启预处理,但一旦用到 raw()、extra()、DB::select(DB::raw(...)),就退出安全路径。而代理层不信任任何上层声明,它只认最终发来的二进制协议包。
- 即使业务代码写了
whereRaw("status = '$status'"),代理仍能按规则拦截 - 某些遗留系统用 JDBC
Statement拼接 SQL,驱动层没走 prepare 流程,代理是最后一道防线 - 日志采集、审计系统直连数据库时,代理还能补上 SQL 审计字段(如
user@host,client_application_name)
真正难的不是加一层代理,而是让所有流量——包括定时任务、运维脚本、BI 工具——都必须经过它。漏掉任意一个出口,语义级防护就形同虚设。











