不展开变量的指令中的变量¶
Gixy Check ID: non_expanding_variable
许多 NGINX 指令会展开变量,因此很容易以为所有指令都会这样做。
map 的值可以包含变量,
而 geo 和
split_clients
的值不能:NGINX 会把 $binary_remote_addr 保存为 19 个字符的字面字符串
$binary_remote_addr。nginx -t 不会给出任何警告。
共享的限速桶¶
下面的配置看起来像是带白名单的按客户端限速:
geo $limit_key {
default $binary_remote_addr; # 按字面保存!
127.0.0.1 "";
}
limit_req_zone $limit_key zone=dynamic:10m rate=5r/s;
白名单之外的所有客户端都得到同一个键 $binary_remote_addr,因此共享一个桶。
一次爬虫突发就会耗尽限额,真实访客会收到 503。Gixy 将其报告为 HIGH,
并指出使用该键的 limit_req_zone 或 limit_conn_zone 指令。
更安全的写法¶
让 geo 只负责分类客户端,再由链式 map 展开变量。这是
NGINX 限速指南中的白名单模式:
geo $limit {
default 1;
127.0.0.1 0;
10.0.0.0/8 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=dynamic:10m rate=5r/s;
split_clients 也采用同样的方法:返回字面标记,再用 map 映射到变量值。
永远不会展开的匹配模式¶
location、server_name、rewrite 和 if 的模式在 NGINX 加载配置时只编译一次,
其中的变量不会被替换。Gixy 将其报告为 MEDIUM:
set $admin admin;
# 前缀 location:只匹配字面 URI "/$admin/"
location /$admin/ { deny all; }
# 正则:“$” 是字符串结尾锚点,因此 "^/$admin/" 永远不会匹配,
# deny 会被悄悄跳过
location ~ ^/$admin/ { deny all; }
rewrite ^/$old/(.*)$ /new/$1 permanent; # 永远不会触发
if ($uri ~ $blocked) { return 403; } # 正则就是文本 "$blocked"
server_name $tenant.example.com; # 只匹配 Host "$tenant.example.com"
请直接写出值,或把比较移到 map 中:
location ~ ^/admin/ { deny all; }
map $uri $blocked_uri {
default 0;
~^/private 1;
}
转义为 \$ 或位于 [$] 这样的字符类中的美元符号是普通字符,不会被报告。
server_name $hostname 是文档中规定的特殊名称,可以使用。
auth_request 的 URI¶
auth_request 会把子请求按原样发送到所写的 URI。使用
auth_request /auth/$host; 时,子请求会发往 /auth/$host。如果任何 location
对该 URI 返回 2xx,所有请求都会被放行,因此 Gixy 将其报告为 HIGH。
请使用固定 URI,并通过请求头把数据传给认证 location:
location / {
auth_request /auth;
}
location = /auth {
internal;
proxy_set_header X-Original-Host $host;
proxy_pass http://auth-backend;
}
未渲染的环境变量占位符¶
NGINX 不会在配置中读取环境变量。没有被 set、map 或其他指令定义的
${UPPER_CASE} 名称几乎总是未被渲染的模板占位符:
# 从 Docker 模板复制而来,但没有运行 envsubst
proxy_pass http://${BACKEND_HOST}:8080;
Gixy 将其报告为 MEDIUM。请在 NGINX 启动前用 envsubst 渲染模板。
官方 Docker 镜像会对 /etc/nginx/templates/*.template 执行这一步。
不支持变量的路径¶
以下指令按字面使用路径,会创建名称中带 $ 的文件或目录。Gixy 将其报告为 LOW:
| 指令 | 按字面使用的参数 |
|---|---|
error_log |
文件路径 |
client_body_temp_path、proxy_temp_path、fastcgi_temp_path、uwsgi_temp_path、scgi_temp_path |
目录 |
proxy_cache_path、fastcgi_cache_path、uwsgi_cache_path、scgi_cache_path |
目录 |
# 会创建一个名为 "$host.error.log" 的文件
error_log /var/log/nginx/$host.error.log;
access_log 支持变量,因此不会被报告。map 值、add_header、proxy_set_header、
root、alias、try_files、proxy_pass 以及 limit_req_zone 的键同样会展开变量。
在问题进入生产环境之前发现它
GetPageSpeed Amplify 按计划在每台 NGINX 主机上运行 Gixy,并在出现新问题和 CVE 时发出告警,同时展示运行时指标。 设置持续监控.