应使用 $uri 替代 $request_uri 进行路径匹配,因其已解码且不含查询参数和分号后缀;对多重编码需结合 Lua 递归解码;同时禁用分号解析、规范化路径并拦截非常规编码与非法字符。

直接在 $request_uri 上做正则匹配,拦不住双重编码、分号绕过这类请求。因为 Nginx 默认不自动解码 URI 中的百分号编码(如 %252f 是 %2f 的二次编码),更不会处理分号截断逻辑——而 Tomcat 等后端会解码、会忽略分号后内容。所以网关看到的是“变形体”,后端还原成“真身”,防护就漏了。
用 $uri 替代 $request_uri 做匹配
$uri 是 Nginx 内部标准化后的路径,已做一次解码(如 %2f → /),且不含查询参数和分号后缀。它比原始 $request_uri 更接近后端实际收到的路径,是更可靠的匹配依据。
- 错误写法(易被绕过):
if ($request_uri ~* /actuator) { return 403; } - 推荐写法:
if ($uri ~* ^/actuator) { return 403; } - 注意:必须加
^锚定开头,避免匹配到/api/v1/actuator-test这类合法路径
主动解码后再检查(Nginx + Lua 方案)
当攻击使用双重或多次编码(如 %252f → %2f → /),单靠 $uri 不够。这时需在 Nginx 中嵌入 Lua 脚本,对 URI 做递归解码并重写,再判断。
- 启用
nginx-lua-module(OpenResty 自带) - 在
location或server块中添加:
local decoded = ngx.unescape_uri(ngx.var.uri)
if decoded ~= ngx.var.uri then
-- 若解码后变化,说明存在编码,再检查是否含敏感路径
if string.match(decoded, "^/actuator") then
ngx.exit(403)
end
end
}
该逻辑可扩展为循环解码,直到无变化为止,有效对抗多层编码。
禁用分号解析并统一规范化路径
Nginx 默认保留分号,但 Tomcat 会截断。要消除这个差异,可在 server 或 location 块中关闭分号语义,并强制标准化:
- 添加
merge_slashes off;(防止路径中//被合并干扰匹配) - 用
rewrite清除分号及之后内容:rewrite ^(/[^;]*)\;.*$ $1 break; - 再配合
$uri匹配,确保所有带分号的/actuator;xxx都被规整为/actuator后拦截
补充:拒绝非常规编码与非法字符
除了 actuator,还应阻断含异常编码格式的请求,比如非标准十六进制、空字节、控制字符等:
- 检测未完成编码:
if ($request_uri ~ "%[^0-9A-Fa-f]{2}") { return 400; } - 拒绝含
%00(空字节)、%2e%2e(目录遍历)等高危模式:if ($uri ~ "\.\./|%00|%(2e){2}") { return 403; } - 建议将这类规则放在所有
location之前,全局生效


















