nginx 无法真正防 sql 注入,仅能基于 $args 和 $request_uri 对明文攻击特征(如 ' or 1=1、union select)做关键词粗筛并 return 403,不解析语义、不读 post 体,必须配合 pdo 预处理等应用层防护才有效。

Nginx 本身不解析 SQL,也不能真正“防注入”,它只能在最外层对 URL 和查询参数做关键词粗筛。这种配置不能替代应用层防护(如 PDO 预处理),但能快速拦截扫描器发出的显性攻击,比如 ' or 1=1、union select 这类明文请求。
只对 $args 和 $request_uri 做匹配
Nginx 默认拿不到 POST 请求体($request_body 为空),所以所有规则必须基于这两个变量:
-
$args:已解码的查询字符串,例如id=1%20or%201=1→ 解码后是id=1 or 1=1,适合匹配or 1=1 -
$request_uri:原始 URI(含编码),例如/api?id=1%20union%20select→ 要匹配%20union%20select或union[[:space:]]+select
推荐的基础正则规则(放在 server 或具体 location 块内)
用 return 403 终止请求,别用 rewrite,避免被绕过:
if ($args ~* "(%27)|(\')|(--)|(%23)|(#)|\b(select|union|insert|update|delete|drop|exec|declare|cast|convert)\b") {
return 403;
}
if ($request_uri ~* "(%20OR%20|%20AND%20|/\*|\*/|;\s*(select|union)\s+from)") {
return 403;
}
关键点说明:
- 用
\b匹配单词边界,防止误伤user_id、comment等正常字段 -
%20OR%20比OR更可靠,因空格常被 URL 编码 - 不匹配
order by、limit、where等合法关键词,减少误杀 - 正则用
~*(忽略大小写),否则UnIoN就漏掉了
必须避开的常见错误
- 把
if写在http{}块顶层:Nginx 会报错或规则不生效 - 在
location ~ \.php$里写规则,却用 raw 字符去匹配已解码的$args - 正则中漏转义括号、点号等特殊字符,导致实际没匹配上
- 试图用
$request_body判断:普通 Nginx 配置下该变量为空,规则无效
真正该优先做的事
这些比折腾 Nginx 规则更有效:
- PHP 层全部使用 PDO 预处理语句,参数用
bindValue()绑定 - MySQL 创建专用账号,只给必要权限(如仅
SELECT,INSERT) - 禁用
eval()、system()、exec()等危险函数
不复杂但容易忽略











