Nginx 上 ModSecurity 不是插件,必须通过动态模块加载且确认 nginx -V 输出含 modsecurity;需启用 SecRequestBodyAccess On 并正确配置 proxy_pass_request_body 或 fastcgi_pass_request_body,否则 SQLI 规则无效。

ModSecurity 在 Nginx 上根本不是“插件”,不能像 Apache 那样直接加载 mod_security 模块。你看到的任何“LoadModule security2_module”配置,或直接在 nginx.conf 里写 SecRule 的做法,都会报错:nginx: [emerg] unknown directive "SecRule"——这是最典型的误配信号。
为什么 nginx -V 看不到 modsecurity 就别往下试了
模块没编译进去,所有规则配置都无效。Nginx 原生不支持 ModSecurity v2/v3 的 Apache 风格指令。
- 必须确认
nginx -V 2>&1 | grep -o modsecurity输出非空(比如含--add-dynamic-module=../nginx-modsecurity) - Debian/Ubuntu 用户可直接装
nginx-mod-http-modsecurity包,它已预编译好 connector;CentOS/RHEL 则基本要自己源码编译libmodsecurity+nginx-modsecurity - 用
ldd $(which nginx) | grep modsecurity可验证动态链接是否成功,若无输出,说明模块未真正加载
SecRequestBodyAccess On 是 SQLI 规则生效的前提
OWASP CRS 的 REQUEST-942-APPLICATION-ATTACK-SQLI.conf 默认不检查 POST body,因为 SecRequestBodyAccess 默认是 Off。你在 Nginx 里漏掉这行,等于关掉了 70% 的 SQL 注入检测能力。
- 在
modsecurity.conf中显式写:SecRequestBodyAccess On - 若后端是 PHP-FPM,还需在
location块中加:proxy_pass_request_body on;和fastcgi_pass_request_body on;,否则 Nginx 不把原始 body 传给 PHP,ARGS变量始终为空 - 注意 multipart/form-data 请求:Nginx connector 对文件上传参数解析不完整,CRS 的
ARGS_NAMES可能漏匹配,建议额外加一条针对REQUEST_BODY的兜底规则,如:SecRule REQUEST_BODY "@rx (?i)(union\s+select|select\s+.*\s+from)" "id:1002,phase:2,deny,status:403,msg:'Raw SQLi in body'"
绕过 CRS 的常见手法和对应补救
攻击者用 sel%65ct、UNI/**/ON SEL/**/ECT 或双 URL 编码,CRS v3.3+ 虽有 t:urlDecodeUni,但在 Nginx 的请求链路中容易丢失二次解码环节。
- 确保规则中明确启用转换链,例如:
SecRule ARGS "@rx (?i)union\s+select" "t:urlDecodeUni,t:lowercase,id:1003,phase:2,deny" - 避免只依赖单条规则阻断;CRS 使用累加式异常分值(
TX:ANOMALY_SCORE),需检查是否启用了最终拦截规则:SecRule TX:ANOMALY_SCORE "@gt 5" "id:1000,deny,status:403,msg:'SQLi detected'" - 日志里若频繁出现
Matched "union select"却没拦截,大概率是phase:2规则被跳过——确认modsecurity_rules_file加载顺序在modsecurity on;之后,且没被SecRuleEngine Off覆盖
别信“一键安装 mod_security”的脚本
很多网上脚本直接复制 Apache 的配置结构,往 http{} 块里硬塞 SecRule,或试图用 load_module 加载 libmodsecurity.so —— 这在 Nginx 下完全无效。
- 真正可用的加载方式只有一种:
load_module modules/ngx_http_modsecurity_module.so;(路径必须与编译时--modules-path一致) -
modsecurity_rules_file必须指向纯文本规则文件,不能是 Apache 的.conf(含<ifmodule></ifmodule>等语法会解析失败) - CRS 规则需从 coreruleset 主分支下载,v3.3+ 才对 Nginx connector 有基本适配;v3.2 及更早版本默认禁用
SecRequestBodyAccess,且无t:urlDecodeUni支持
实际生效的关键不在规则多寡,而在请求体是否真正进入检测管道、解码是否完整、拦截逻辑是否串联。多数线上环境卡在第一步:body 没传过去,或者模块压根没加载。先跑通 nginx -t 和 curl -X POST -d "x=1' OR '1'='1" <a href="https://www.php.cn/link/5830bc2bbfda076fdc00e649f2c82e1f">https://www.php.cn/link/5830bc2bbfda076fdc00e649f2c82e1f</a> 看日志有没有匹配记录,再谈调规则。


















