nginx仅能通过正则匹配$args和$request_uri拦截显性sql注入特征,无法解析语义或读取post体,应聚焦union select、'、--等高危模式,配合map+if机制与编码兼容性处理,作为轻量前置快筛防线。

Nginx 本身不解析 SQL 语义,也不能直接读取 POST 请求体(除非启用 Lua 或 ModSecurity),但它能在请求抵达后端前,对 $args(查询参数)和 $request_uri 这两个最常被用于注入的字段做高效、低开销的正则匹配。这是一道轻量但关键的前置防线,重点不是“100%防住所有变种”,而是快速拦截大量脚本小子的显性探测行为。
只匹配真正高危且极少出现在合法业务中的模式
很多规则误杀率高,是因为把 and、or、where 这类通用词也纳入了拦截。实际应聚焦在:
- 明确用于攻击的 SQL 关键字组合(如
union select、insert into、drop table) - 非法语法符号(单引号
'、分号;、双破折号--、井号#) - URL 编码的尖括号
%3C%3E(常见于 XSS+SQL 混合探测) - 典型函数调用(
sleep(、benchmark(、load_file()
推荐放在 server 块内,用 map + if 组合方式
避免在 location 外大量写 if,更推荐先用 map 提前标记风险,再统一拦截:
map $args $sql_inject {
~*[\"'`;]|(--)|(%23)|(#)|(%27) 1;
~*(?i)(union\s+select|insert\s+into|drop\s+table|select\s+.*\s+from|exec\s+xp_|load_file|sleep\(|benchmark\() 1;
default 0;
}
map $request_uri $uri_inject {
~*../|/etc/passwd|/phpmyadmin|/wp-admin|/mysql|/phpinfo 1;
~*(%3C|%3E|<script default><p>然后在 <code>server 或具体 <code>location 中统一拦截:<pre class='brush:nginx;toolbar:false;'>if ($sql_inject = 1) { return 403; }
if ($uri_inject = 1) { return 403; }</script>
注意几个容易失效的关键细节
- 正则必须加
(?i)或用~*实现忽略大小写,否则UnIoN SeLeCt就会漏掉 - 空格要写成
\s+或[%20\+],因为攻击者常用 URL 编码或加号替代空格 - 单引号
'在$args中可能已被解码,但%27也可能残留,建议两者都匹配 - 不要试图用
rewrite匹配$query_string,它只作用于 URI 路径部分;必须用if ($query_string ...)
配合基础防护提升实效性
光靠关键词不够,需叠加以下措施防止绕过:
- 限制请求体大小:
client_max_body_size 10m;(防大 payload 绕过 args 检测) - 控制请求头长度:
client_header_buffer_size 1k; large_client_header_buffers 4 8k; - 封禁高频试探 IP:用
limit_req_zone $binary_remote_addr zone=sql_limit:10m rate=1r/s; - 记录拦截日志:
log_format sql_log '$remote_addr [$time_local] "$request" $status $sql_inject $uri_inject';
Nginx 层的拦截本质是“快筛”,不是“深检”。它能挡住 70% 的自动化扫描和初级探测,为后端节省大量无效计算。真正的深度防护仍需应用层参数校验 + WAF(如 ModSecurity)协同。











