WAF拦截SQL注入的前提是请求体必须完整送达,需启用SecRequestBodyAccess On、后端配置proxy_pass_request_body on、结合异常分数机制与语义引擎(如libinjection)进行两段式检测,而非仅依赖正则匹配。

WAF网关拦截SQL注入的前提是请求体必须完整送达
很多团队在微服务网关(如 Spring Cloud Gateway、Kong、Nginx+ModSecurity)上配了规则,却始终拦不住 POST /api/user/search 这类 JSON 接口的注入,根本原因是 WAF 默认不解析原始请求体。攻击载荷藏在 {"q": "admin' OR 1=1--"} 里,但 WAF 只扫了 URL 和 Cookie,REQUEST_BODY 变量为空——规则自然不触发。
实操要点:
-
SecRequestBodyAccess On必须写在 ModSecurity 全局配置(如modsecurity.conf)顶层,不能只放在某个location块内 - 后端是 Spring Boot(经
proxy_pass):对应location块加proxy_pass_request_body on; - 后端是 PHP-FPM:在
location ~ \.php$块中加fastcgi_pass_request_body on; - 若用 JSON,还需确认 WAF 启用了
SecResponseBodyAccess On和 JSON 解析器(如SecResponseBodyMimeType application/json)
别直接开全 OWASP CRS 的 SQLI 规则集
CRS 4.x 的 REQUEST-942-APPLICATION-ATTACK-SQLI.conf 规则很强,但默认动作是 log,tag:'OWASP_CRS',不是 deny;且对 multipart/form-data 解析支持弱,上传接口带参数时极易漏检。更麻烦的是,942100(LIKE 检测)会把正常搜索 ?q=%xxx% 全拦成 403。
建议做法:
- 上线前先设
SecRuleEngine DetectionOnly运行 3–5 天,盯modsec_audit.log里的误报 - 对高频误杀规则临时禁用,例如:在规则前加
SecRuleRemoveById 942100 - 真正启用拦截时,优先用异常分数机制:
SecRule TX:ANOMALY_SCORE "@gt 5" "id:1000,deny,status:403,msg:'SQLi detected'" - 不要全局开,而是按路径收紧:比如只对
/api/.*user.*或/login启用严格模式
单靠正则匹配 union select 是拦不住真实攻击的
攻击者早就不发裸关键字了。UnIoN%09SeLeCt、SEL/**/ECT、0x556e696f6e2053656c656374 这些变体能轻松绕过 @rx union\s+select 这类规则。单纯正则既易漏又易误杀(比如产品名含 ORANGE 就被拦)。
有效策略是两段式检测:
- 第一阶段用轻量正则初筛:
ARGS|REQUEST_BODY "@rx [\"'`][^&\n\r]{0,50}(?:union|select|sleep|benchmark)",只抓带引号+疑似关键词的请求,降低性能损耗 - 第二阶段交由语义引擎判断,如调用
libinjection_is_sqli:SecRule &REQUEST_BODY "@eq 1" "chain,id:2000,deny,msg:'SQLi via libinjection'"SecRule REQUEST_BODY "@inspectFile /usr/local/modsecurity/libinjection.data" - 云 WAF(如腾讯云、华为云)可直接用内置 AI 引擎,条件写成:
request_uri matches "/api/.*" and libinjection_is_sqli == true
网关层拦截只是应急,不是修复
Spring Cloud Gateway 里加个 SqlInjectionFilter 能快速兜底,但它本质是“识别已知坏”,对宽字节注入(%A1%AA)、多层编码(%2527)、协议解析差异(WAF 解码方式 vs 数据库实际解析)完全无感。WAF 日志里反复出现的放行请求,比如 id=1%20OR%201=1,恰恰暴露了代码里最危险的拼接点——你该立刻去搜 request.queryParams.get("id") 或 @Param("id"),而不是再加一条更狠的正则。
真正要盯死的,是那些 WAF 拦不住、但日志里高频出现的参数名。它们往往直连 ORDER BY 或 LIMIT 子句,参数化查询救不了,只能上白名单校验。











