精准拦截sql注入需先解析解码再语义分析:开启请求体解析与归一化,显式调用t:urldecodeuni/t:lowercase,启用libinjection等语义引擎并绑定数据库类型,两段式规则+指纹验证确保生效。

光靠 union|select 这类正则匹配,90% 的变形 SQL 注入都会漏掉——因为真实攻击根本不会直写 UNION SELECT,而是用 UnIoN%09SeLeCt、SEL/**/ECT、%73elect 等方式绕过。精准拦截的关键不是堆规则,而是让 WAF 先“看懂”请求,再决定拦不拦。
必须开启请求体解析和解码归一化
大量 SQL 注入藏在 POST body 或 JSON 里,若没开解析,WAF 根本看不到载荷。
-
SecRequestBodyAccess On要写在全局modsecurity.conf,不能只放在location块里 - 后端是 PHP-FPM,必须加
fastcgi_pass_request_body on;是 proxy_pass(如 Node.js),得配proxy_pass_request_body on - 规则中必须显式调用
t:urlDecodeUni,t:lowercase,否则%27union%20select和UnIoN都匹配不上 - JSON 接口要额外确认 WAF 是否启用了 raw body 解析,否则
{"id":"1' OR 1=1--"}完全不进检测流程
别单独依赖正则,必须用语义引擎做第二道判断
正则只能筛形似,语义引擎(如 Libinjection)才能判真伪。比如 SELECT * FROM posts 是合法搜索,admin' AND (SELECT COUNT(*) FROM users)>0-- 才是布尔盲注——仅靠关键词匹配无法区分。
- 阿里云/腾讯云/华为云 WAF 的语义分析默认常处于关闭或观察模式,需手动开启,且要绑定数据库类型(MySQL/MSSQL 语法差异大)
- 宝塔 WAF 启用「高级 SQL 防护」后,底层才调用类似
libinjection_is_sqli的函数 - 自定义规则建议设为两段式:
SecRule ARGS "@rx [\"'`].{0,50}(?:union|select|sleep)" "phase:2,pass,setvar:tx.sqli_flag=1",再接一条SecRule TX:SQLI_FLAG "@eq 1" "phase:2,block,ctl:ruleEngine=On,chain"并在 chain 中调用语义函数 - 语义引擎生效的硬指标:WAF 日志里拦截记录含
fingerprint字段,且同一载荷在开关语义前后行为必须变化(从放行→拦截)
绕过高频点:空格、注释、编码必须显式覆盖
攻击者不用空格,改用 %09(Tab)、%a0(NBSP)、/**/;不写 --,改用 # 或 %23;大小写混用是基础操作。正则不提前处理这些,等于白写。
- 空格类分隔符应统一用
[\s%09%0a%0b%0c%0d\xa0]匹配,不能只写\s - 注释符要覆盖
(?:--|%23|/\*)\s*[\r\n\t ]*,否则id=1%23foo%0D%0Aunion%20select就会漏 - 避免用
.*匹配长字段,改用[^&\n\r]{0,100}限制长度,防回溯爆炸拖慢整条规则链 - 禁用宽泛表达如
args contains "OR 1=1"——它会拦住所有含OR的合法参数(如?filter=ORANGE)
最常被忽略的是:语义引擎是否真正加载并参与了决策。很多团队看到控制台显示“已启用”,就以为万事大吉,结果日志里连 fingerprint 都没有,说明底层词法分析器根本没跑起来。











