SQL注入拦截必须在数据库协议层做,应用层过滤靠不住;ProxySQL是生产级选择,支持热加载规则、SQL指纹匹配及正则扫描,需配置大小写不敏感、GBK编码适配、禁用多语句等兜底措施。

SQL注入拦截必须在数据库协议层做,应用层过滤靠不住
应用层用 PreparedStatement 或 ORM 参数化查询确实能防大部分注入,但只要存在动态拼接 SQL 的路径(比如老系统、报表引擎、权限中间件),或绕过应用直连数据库(如 DBA 工具、ETL 任务),就等于把大门敞开。真正能兜底的,只有数据库代理层——它能看到完整协议流,且不依赖业务代码改造。
选 MySQL Proxy 还是 ProxySQL?优先 ProxySQL
MySQL Proxy 已停止维护,Lua 脚本写规则难调试,高并发下性能抖动明显;ProxySQL 是生产级选择,支持规则热加载、SQL 模式匹配、响应时间统计,且兼容 MySQL 协议零修改。
- 规则写在
mysql_query_rules表里,用match_pattern匹配危险语法,例如.*unions+select.*、.*;\s*drop\s+table.* - 匹配后设
destination_hostgroup = -1并apply = 1,即可直接拒绝请求,返回Access denied for user类错误(实际可自定义) - 注意:正则要加
case_sensitive = 0,否则大小写混写的UnIoN就漏过了 - 别盲目开全量日志,
mysql-eventslog开启后会显著拖慢吞吐,只对error级别规则启用
match_digest 比 match_pattern 更稳,但得理解它的局限
match_digest 对 SQL 做标准化(去空格、转小写、参数占位),匹配的是“指纹”,比正则更抗变形。比如 SELECT * FROM users WHERE id = 1 和 select
*
from users where id=1 指纹一致。
- 适合拦截已知恶意模板,比如
SELECT ... FROM ... WHERE ... OR 1=1类永真条件 - 但它无法识别注释绕过:
SELECT/*abc*/1和SELECT 1指纹不同,得额外加规则 - 遇到
UNION SELECT load_file('/etc/passwd')这种带函数调用的,match_digest会把函数参数抹掉,变成UNION SELECT load_file(?),所以还得配合match_pattern扫描函数名
别忘了绕过检测的常见手法和对应补丁
攻击者早就不硬刚 union select 了,代理层规则得覆盖这些变体:
- 用注释分隔关键字:
U%00NION/**/SEL%00ECT→ 规则里正则要加[\s\x00\/*]+类空白符容忍 - 双写绕过:
UUUION SELELECT→ 用replace预处理或正则U+N+I+O+N+(但慎用,可能误杀) - 宽字节注入(如 GBK):代理层默认按 UTF-8 解析,若后端是 GBK,需在
ProxySQL的mysql_servers表里设charset = gbk,否则无法正确识别截断点 - HTTP header 注入(如
X-Forwarded-For: 127.0.0.1' OR SLEEP(5)--)→ 这类根本到不了代理层,得在 WAF 或 API 网关拦,代理层只管 MySQL 协议流量
真实环境里,最常被忽略的是多语句支持(multiple-statements=1)。只要客户端开了这个选项,一条 SELECT 1; DROP TABLE users; 就能绕过所有单语句匹配规则——必须在 ProxySQL 的 mysql_users 表里设 max_connections 和 default_hostgroup 同时关闭 transaction_persistent,并确保后端 MySQL 的 sql_mode 包含 STRICT_TRANS_TABLES。

















