Nginx本身不解析SQL,防注入只有两条路:一是用if+return做关键词粗筛,仅拦截显性攻击且不读请求体;二是编译加载nginx-modsecurity实现深度检测,但需严格配置SecRequestBodyAccess On、SecRuleEngine On及CRS规则,缺一不可。

直接上结论:Nginx 本身不解析 SQL,也不能靠改几行配置就“自动防注入”。真要拦截,只有两条路——用 if+return 做关键词粗筛,或编译加载 nginx-modsecurity 做深度检测。前者快但易绕过,后者强但配置错一步就完全不生效。
用 if + return 拦显性 SQL 注入特征
这是最轻量、上线最快的方式,适合快速挡掉扫描器批量发的 ' or 1=1、union select 这类明文攻击。它只检查 $args(查询参数)和 $request_uri,不读请求体,所以对 POST JSON 或表单提交无效。
- 正则必须用
~*(忽略大小写),否则UnIoN就漏掉了 -
$query_string和$request_uri是两个不同变量:前者不含 path,后者含完整 URL,别混用 - 别在
if里写rewrite,容易被绕过;统一用return 403或return 444(直接断连,更省资源) - 常见误配:把
if写在http{}块顶层,没包在server或location里,Nginx 会报错
示例(放在 server 块内):
if ($args ~* "(%27)|(\')|(--)|(%23)|(#)|(\b(SELECT|UNION|INSERT|DROP|UPDATE|DELETE|EXEC|DECLARE)\b)") {
return 403;
}
if ($request_uri ~* "(union\s+select|select\s+\*\s+from|or\s+1\s*=\s*1)") {
return 403;
}启用 nginx-modsecurity 真正阻断变形攻击
想防 Base64 编码、URL 二次编码、注释符分隔(如 uni/**/on sel/**/ect)这类绕过,必须上 nginx-modsecurity。这不是装个包就行的事——它是个动态模块,得从源码编译 Nginx 或用已集成该模块的发行版(如 Debian 的 nginx-mod-http-modsecurity)。
- 运行
nginx -V 2>&1 | grep -o modsecurity,输出必须有modsecurity,否则模块根本没加载 -
load_module路径必须绝对准确,比如modules/ngx_http_modsecurity_module.so,路径错一个字符就报unknown directive "SecRule" -
modsecurity on;必须写在server或location块里,不能只写在http{}顶层 - CRS 规则默认不阻断,必须显式设
SecRuleEngine On,且补上deny,status:403动作
关键配置项:SecRequestBodyAccess 和 fastcgi_pass_request_body
90% 的 ModSecurity 在 Nginx 上“看似启用实则失效”,根源就在这两项没开。CRS 的 SQLI 规则(如 REQUEST-942-APPLICATION-ATTACK-SQLI.conf)依赖 ARGS 和 REQUEST_BODY 变量,而 Nginx connector 默认关闭请求体解析。
- 必须在
modsecurity.conf中设置SecRequestBodyAccess On,否则所有 POST 参数、JSON body 都为空 - 后端是 PHP-FPM?还得在
location ~ \.php$块里加fastcgi_pass_request_body on;,否则 Nginx 不转发原始 body 给 PHP - 规则加载别用
Include,用modsecurity_rules_file /path/to/crs/rules.conf - CRS v3.3+ 默认
SecRuleEngine DetectionOnly,只记日志不拦截,必须手动改成On
绕过检测的典型场景和补救动作
攻击者早就不硬刚 select * from 了。他们用大小写混用、%u0061 Unicode 编码、/*abc*/ 注释分隔、甚至宽字节截断来绕过正则。Nginx 层规则越激进,误伤率越高——比如拦掉 order by id 这种合法语句。
- 开启
SecUnicodeMapFile unicode.mapping 20127并配好映射,避免 UTF-8 双字节绕过 - 补一条兜底规则:
SecRule TX:ANOMALY_SCORE "@gt 5" "id:1000,deny,status:403,msg:'SQLi detected'",靠累计评分触发阻断 - 自定义规则必须带
phase:2,否则ARGS变量不可用;没这 phase,规则压根不执行 - 每条请求多耗 5–15ms CPU,高并发前务必压测,别等上线才发现 TTFB 翻倍
真正难的不是写规则,而是确认每层都生效:模块加载了没?请求体读取开了没?规则文件路径对不对?deny 动作加了没?漏掉任意一环,防护就是假象。


















