1. 项目概述从一次紧急告警说起那天晚上我正在处理一个线上服务的性能调优突然监控告警响了不是CPU飙升也不是内存泄漏而是一条安全扫描的告警“疑似存在Actuator端点未授权访问风险”。我心里咯噔一下Actuator这玩意儿我熟啊Spring Boot的监控利器但要是暴露在外网那简直就是给黑客开了个后门。我赶紧登录服务器检查发现Nginx的配置里确实有对/actuator路径的代理但配置了deny all。奇怪按道理应该防住了。我用curl随手试了几个变形路径比如/actuator/、/actuator..;/、/actuator%2f结果当请求/actuator%2fhealth时服务竟然返回了健康状态信息问题找到了这就是典型的“字符绕过漏洞”。攻击者通过构造特殊的字符如/的URL编码%2f或;、..等可以绕过Web服务器如Nginx或API网关如Apache APISIX中基于路径前缀匹配的安全规则直接访问到本应被禁止的后端敏感接口。这个项目就是围绕如何彻底堵上这个安全漏洞展开的。它不仅仅是加一条deny指令那么简单而是需要深入理解Nginx和APISIX的配置语法、请求处理流程以及不同字符在HTTP请求解析中的微妙差异。无论是运维工程师、开发人员还是安全负责人只要你负责的线上服务用到了Spring Boot Actuator并通过Nginx或APISIX对外暴露这篇文章里详尽的配置方案和避坑经验都能帮你构建起更坚固的第一道防线。我们将从漏洞原理拆解开始一步步深入到Nginx和APISIX的具体、可落地的安全配置实践中。2. 漏洞核心原理与风险场景拆解2.1 Actuator端点为何成为靶子Spring Boot Actuator提供了一系列生产就绪的特性主要用于监控和管理应用。它暴露的端点如/actuator/health健康检查、/actuator/env环境变量、/actuator/metrics指标甚至/actuator/heapdump堆转储对于运维来说是无价之宝。然而这些端点如果未经保护直接暴露风险极高。/actuator/env可能泄露数据库密码、API密钥/actuator/heapdump可能包含内存中的敏感数据/actuator/loggers甚至可以动态修改日志级别干扰系统运行。因此最佳实践是在应用层通过Spring Security进行严格的认证和授权。但现实中很多团队会选择在网关或反向代理层进行“一刀切”的屏蔽认为这样更简单、对应用无侵入。正是这种“图省事”的思路遇到了路径解析的复杂性才催生了字符绕过漏洞。2.2 字符绕过的“花式”手法字符绕过漏洞的本质是配置的匹配规则与Web服务器/网关的实际解析规则不一致。攻击者利用的是这种“认知差”。以下是一些常见的绕过手法斜杠编码绕过这是最常见的一种。Nginx配置中location /actuator { deny all; }其意图是匹配以/actuator开头的路径。但攻击者请求/actuator%2fhealth。这里的%2f是/的URL编码。在某些默认配置下Nginx在匹配location时可能不会自动解码%2f因此不认为该请求以/actuator开头从而绕过规则。而后端的Tomcat或Netty服务器在收到请求时通常会进行URL解码将%2f还原为/于是请求就被正确路由到了/actuator/health端点。路径回溯绕过利用..进行路径回溯。例如请求/actuator/../health。经过规范化normalization后这个路径等价于/health。如果Nginx的配置是简单地匹配/actuator前缀然后deny那么这个请求在Nginx看来是/health不被拦截。但有些后端服务或特定的配置下可能不会进行严格的路径规范化导致异常解析。分号参数绕过在路径中插入分号;。例如/actuator;/health。根据旧的HTTP规范和某些服务器的实现;有时会被视为路径参数path parameters的分隔符路径部分在;之前。Nginx的location匹配可能只看到/actuator而Java应用服务器可能会将/actuator;/health解析为访问/actuator/health。多重编码与大小写混淆更复杂的攻击会使用双重URL编码如%252f代表%2f再解码为/或者混合大小写如/AcTuAtOr试图绕过一些不够严谨的正则表达式匹配。注意并非所有Nginx或APISIX的版本和配置都会存在这些绕过问题。漏洞是否成立高度依赖于具体的配置指令、版本以及后端应用服务器的行为。我们的目标是通过配置消除这些不确定性实现无论攻击者如何变形都能精准拦截。2.3 影响范围与严重性评估这个漏洞的影响是直接的导致敏感的Actuator管理接口暴露在公网。其严重性取决于暴露的端点类型高风险暴露env,heapdump,trace,logfile等端点可能导致直接的信息泄露甚至远程代码执行如果结合其他漏洞。中风险暴露health,info,metrics等端点可能泄露系统内部状态、组件版本信息为后续攻击提供情报。低风险仅暴露health端点且不包含详情风险相对较低但仍会暴露服务存活状态。即使你认为Actuator端点已在内网只要流量经过Nginx/APISIX且配置存在瑕疵来自内部的横向移动攻击同样可能利用此漏洞。3. Nginx 安全配置实战与深度解析Nginx的配置灵活且强大但“能力越大责任越大”不严谨的配置就会留下缝隙。下面我们构建一个从简单到坚固的多层防御配置。3.1 基础但脆弱的配置为何deny all会失效很多人的第一版配置可能是这样的server { listen 80; server_name your-domain.com; location /actuator { deny all; return 403; # 显式返回403 } location / { proxy_pass http://backend-service; # ... 其他代理设置 } }这个配置意图很明确拦截所有访问/actuator的请求。但它为什么会被/actuator%2fhealth绕过呢关键在于location的匹配过程。默认情况下location /actuator进行的是前缀匹配。对于请求/actuator%2fhealthNginx在匹配location时看到的路径字符串字面就是/actuator%2fhealth。它以/actuator开头吗是的从字符串上看/actuator%2fhealth确实以/actuator开头。所以这个配置其实能拦截/actuator%2fhealth我之前的测试结果需要修正更常见的绕过场景是下面这种配置location ~ ^/actuator(/|$) { deny all; }或者当location指令中使用了一些不恰当的修饰符或嵌套时问题才会显现。但为了绝对安全我们不能依赖默认行为需要更精确的武器。3.2 强化配置方案一精确正则表达式匹配使用正则表达式location可以更精确地控制匹配逻辑避免解码歧义。server { listen 80; server_name your-domain.com; # 方案1使用正则表达式匹配拒绝所有包含 /actuator/ 的请求 location ~* ^/actuator(/|$) { deny all; return 403; # 记录日志便于审计 access_log /var/log/nginx/actuator_block.log; } # 方案1的变种更严格匹配 /actuator 后跟任意非空路径 location ~* ^/actuator/(.*)$ { deny all; return 403; } location / { proxy_pass http://backend-service; proxy_set_header Host $host; # 关键传递原始请求URI防止Nginx规范化导致绕过 proxy_set_header X-Original-URI $request_uri; } }配置解析与避坑指南location ~* ^/actuator(/|$)~*表示不区分大小写的正则匹配。^匹配开头(/|$)表示后面要么接一个斜杠要么直接结束即匹配/actuator和/actuator/*。这个配置能有效拦截/actuator、/actuator/、/actuator/health以及/actuator%2fhealth因为正则匹配是在Nginx进行了一定程度的解码之后进行的这里需要澄清。一个重要陷阱Nginx在处理正则表达式location匹配时使用的$uri变量是已经经过解码、规范化后的URI。也就是说对于请求/actuator%2fhealth$uri的值会是/actuator/health。因此上面的正则配置能够成功匹配并拦截。这反而是个好事proxy_set_header X-Original-URI $request_uri;这是一个防御性技巧。$request_uri是原始的、未经解码的请求URI。将其传递给后端后端应用可以记录或检查原始请求用于更高级的安全审计或二次校验。实操心得正则表达式location虽然强大但性能上略逊于前缀匹配。如果访问量极大需要评估影响。不过对于/actuator这类管理端点访问频率极低性能开销可忽略不计。3.3 强化配置方案二利用map指令实现集中化管理当你有多个server块或需要管理大量敏感路径时在每个location里写deny all会显得冗余。使用map指令可以将黑名单逻辑抽象出来使配置更清晰、更易维护。# 在http块中定义映射置于所有server块之外 http { # 定义一个变量 $block_actuator根据$uri进行映射 map $uri $block_actuator { default 0; # 使用正则表达式匹配需要拦截的路径 ~*^/actuator(/|$) 1; # 你可以在这里轻松添加其他敏感路径 ~*^/admin(/|$) 1; ~*^/phpmyadmin(/|$) 1; } server { listen 80; server_name your-domain.com; # 在进入主要location前进行拦截 if ($block_actuator) { deny all; return 403; } location / { proxy_pass http://backend-service; } } }配置解析与避坑指南map $uri $block_actuator创建一个新变量$block_actuator。它遍历$uri如果匹配到右侧的任何一条正则规则则值为1否则为默认值0。if ($block_actuator)谨慎使用if。在Nginx中if是“邪恶的”但在server上下文中用于简单的变量判断和return/deny通常是安全的。这里的作用是如果$block_actuator为1则执行拦截。优势管理方便。增加新的拦截路径只需在map块中添加一行规则。配置逻辑集中一目了然。注意map指令使用的也是规范化后的$uri因此同样能防御编码绕过。3.4 终极加固方案结合location优先级与错误处理最严谨的做法是结合多种手段并考虑边缘情况。server { listen 80; server_name your-domain.com; # 首先显式拒绝所有对 /actuator 的访问包括各种变形 # 使用 进行精确匹配最高优先级 location /actuator { deny all; return 403; } # 使用正则匹配所有 /actuator/ 下的请求 location ~ ^/actuator/(.*)$ { deny all; return 403; } # 其次设置一个兜底的默认拦截处理可能的畸形请求 # 这个location匹配所有请求优先级最低 location / { # 在代理前再次使用if检查原始请求URI$request_uri # 这是一个更保险但性能开销稍大的检查 if ($request_uri ~* ^/[Aa][Cc][Tt][Uu][Aa][Tt][Oo][Rr]) { deny all; return 403; } # 正常的代理设置 proxy_pass http://backend-service; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 强烈建议传递原始URI供后端审计 proxy_set_header X-Original-URI $request_uri; # 隐藏后端服务器头信息增加攻击者难度 proxy_hide_header X-Powered-By; proxy_hide_header Server; } # 最后自定义错误页面避免暴露Nginx版本信息 error_page 403 /403.html; location /403.html { root /usr/share/nginx/html; internal; # 防止直接访问 } }配置深度解析优先级策略Nginx的location优先级为精确匹配 ^~非正则前缀匹配 ~或~*正则匹配 /通用前缀匹配。我们利用最高优先级确保/actuator被精确拦截。双重校验在通用的location /中我们使用if ($request_uri ~* “…”)对原始请求URI$request_uri进行二次正则匹配。$request_uri包含原始查询字符串且未经规范化可以捕捉到一些极其特殊的绕过尝试。注意频繁使用if进行正则匹配对性能有影响需权衡。对于管理端点通常可接受。信息隐藏使用proxy_hide_header移除后端应用如Spring Boot返回的X-Application-Context等头信息这些信息可能暗示Actuator的存在。错误页面自定义403页面避免使用默认页面因为默认页面可能包含Nginx版本为攻击者提供信息。4. Apache APISIX 安全配置实战与策略Apache APISIX作为一个动态、实时、高性能的API网关其配置方式与Nginx静态文件不同主要通过Admin API动态管理。其防护思路类似但实现工具是路由Route和插件Plugin。4.1 理解APISIX的路由匹配机制APISIX中一个请求由哪个“上游”Upstream处理由路由Route的匹配规则决定。路由可以基于URI、Host、Method、Header等多种条件进行匹配。我们的目标是创建一条规则匹配所有试图访问Actuator端口的请求并拒绝它们。APISIX的URI匹配支持两种模式前缀匹配/actuator*。类似于Nginx的前缀匹配但同样可能受到编码问题影响。正则匹配更强大和精确是我们防御的主力。4.2 配置方案使用uri正则匹配与response-rewrite插件最直接的方式是创建一个匹配Actuator路径的路由并配置一个插件直接返回403。通过APISIX Admin API配置curl -X PUT http://APISIX_ADMIN_IP:PORT/apisix/admin/routes/block-actuator \ -H X-API-KEY: YOUR_ADMIN_KEY \ -d { name: block-springboot-actuator, desc: 拦截所有对Spring Boot Actuator端口的访问防止字符绕过, uri: [~* ^/actuator(/|$), ~* ^/actuator%2f.*$], // 关键同时匹配规范化和原始编码路径 plugins: { response-rewrite: { vars: [ [status, , 403] ], body: Access Denied., status_code: 403 } }, upstream: { type: roundrobin, nodes: { 127.0.0.1: 9999 // 可以指向一个不存在的或专门返回错误的上游 } }, priority: 9999 // 设置高优先级确保优先匹配 }配置解析uri: [~* ^/actuator(/|$), ~* ^/actuator%2f.*$]这是防御的核心。我们定义了两个正则条件只要满足其一即匹配。~* ^/actuator(/|$)匹配规范化后的路径如/actuator、/actuator/health。~* ^/actuator%2f.*$直接匹配包含原始编码%2f的请求路径。这是应对Nginx中可能出现的、在匹配阶段未解码情况的强力手段。APISIX的路由匹配逻辑可能允许这样的原始字符串匹配。plugins: { response-rewrite: ... }使用response-rewrite插件。当匹配到该路由时不将请求转发给上游而是直接改写响应返回403状态码和自定义内容。priority: 9999优先级数字越大优先级越高。确保这条拦截路由在匹配到/actuator时优先于其他可能代理到真实后端服务的路由。4.3 进阶方案使用uri-blocker插件或自定义插件除了response-rewriteAPISIX社区还有更专门的插件。方案A使用uri-blocker插件如果已安装或自定义开发uri-blocker插件可以基于URI进行阻断配置更简洁。{ uri: /actuator*, // 前缀匹配作为初始过滤 plugins: { uri-blocker: { block_rules: [^/actuator[/\\s\\S]*, ^.*%2f.*actuator.*$], // 强大的正则阻断规则 rejected_code: 403 } }, upstream: { ... } }block_rules中的正则表达式可以设计得非常严格例如第二条规则^.*%2f.*actuator.*$会阻断任何URI中包含%2f和actuator字样的请求无论顺序防御范围更广。方案B结合proxy-rewrite插件和“黑洞”上游创建一个返回403的独立服务例如一个简单的HTTP服务器或者使用一个无效的上游地址。当匹配到拦截路由时将请求proxy-rewrite到“黑洞”上游。{ uri: [~* ^/actuator], plugins: { proxy-rewrite: { uri: /blocked, // 重写URI到黑洞上游的特定路径 scheme: http, host: 127.0.0.1, port: 8888 // 假设8888端口运行着一个只返回403的服务 } } }4.4 APISIX配置的注意事项与最佳实践优先级管理APISIX的路由匹配顺序受优先级priority和创建时间影响。务必为你所有的拦截路由设置足够高的优先级确保它们在可能冲突的代理路由之前被匹配。测试覆盖配置完成后务必使用各种绕过手法进行测试。curl -v http://APISIX_GATEWAY_IP:PORT/actuator/health curl -v http://APISIX_GATEWAY_IP:PORT/actuator%2fhealth curl -v http://APISIX_GATEWAY_IP:PORT/actuator;/health curl -v http://APISIX_GATEWAY_IP:PORT/AcTuAtOr/health所有请求都应返回403而不是后端服务的响应。动态生效APISIX的配置是动态生效的无需重启。这既是优点也是风险。确保Admin API的访问密钥X-API-KEY得到严格保护。日志与监控为拦截路由启用访问日志并监控其触发频率。异常的频繁拦截可能意味着正在遭受扫描或攻击。5. 通用防御策略与架构思考在网关层配置拦截是重要的防线但并非万无一失。真正的安全需要多层防御Defense in Depth。5.1 应用层加固不可或缺的最后防线网关的规则可能被绕过例如通过未知的编码变形、网关的bug或配置错误因此应用层必须有自己的防护。使用Spring Security保护Actuator端点这是官方推荐且最根本的方式。你可以轻松地集成HTTP Basic认证、OAuth2、JWT等。# application.yml management: endpoints: web: exposure: include: health,info # 仅暴露必要的端点 base-path: /manage # 修改默认的/actuator路径增加隐蔽性 endpoint: health: show-details: when_authorized # 健康检查详情仅对授权用户显示// Security配置类 Configuration public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/manage/health).permitAll() // 健康检查可公开 .antMatchers(/manage/**).hasRole(ACTUATOR_ADMIN) // 其他端点需要特定角色 .and() .httpBasic(); // 使用HTTP Basic认证 } }禁用不必要的端点在生产环境中只暴露真正需要的端点如health用于负载均衡检查。将Actuator端口与业务端口分离通过management.server.port配置让Actuator运行在一个独立的、仅内网可访问的端口上。这是最彻底的隔离方案。5.2 网络层隔离最物理的屏障安全组/防火墙规则在云服务器或防火墙层面严格限制对Actuator端口默认8080或自定义的管理端口的访问只允许特定的运维IP或跳板机访问。内部负载均衡器将包含Actuator的内部端口只暴露给内部的负载均衡器或服务网格如Istio对外只暴露业务端口。5.3 持续监控与响应日志聚合与分析收集Nginx/APISIX的访问日志和应用日志设置告警规则。例如监控对/actuator路径的任何访问包括403请求超过阈值即告警。定期漏洞扫描与渗透测试使用自动化工具如Nessus, Qualys, 或开源的nmap,dirb等定期对公网入口进行扫描模拟攻击者的字符绕过尝试检验配置的有效性。配置即代码与自动化审计将Nginx/APISIX的配置文件纳入版本管理如Git。通过CI/CD流水线在每次变更前进行静态分析例如使用nginx -t测试语法使用自定义脚本检查是否存在不安全的location规则确保安全配置不被意外修改或破坏。6. 常见问题排查与实战调试记录即使配置看起来完美在实际部署中也可能遇到意外。以下是我在多次实战中总结的问题和排查思路。6.1 配置不生效按步骤排查问题现象可能原因排查步骤访问/actuator返回404而非403拦截规则优先级低于其他路由请求被代理到了后端而后端没有该路径。1. 检查Nginx中location块的优先级顺序。确保拦截用的location尤其是精确匹配或正则匹配~定义在通用代理规则location /之前。2. 在APISIX中检查路由的priority字段确保拦截路由的优先级最高。某些编码绕过仍然成功匹配规则不够全面或使用的变量$urivs$request_uri不对。1. 在Nginx配置中增加对$request_uri的检查如方案三中的if语句。2. 在APISIX中在uri匹配规则中显式添加对编码字符的匹配如~* ^/actuator%2f。3.开启调试日志在Nginx中设置error_log logs/error.log debug;观察请求处理流程。返回403但日志里没有记录日志路径配置错误或日志缓冲区未刷新。1. 检查access_log指令的路径和权限。2. 使用tail -f命令实时跟踪日志文件。3. 确认deny all;指令后是否有access_log指令它可能不会被执行。可以将日志记录移到server层级。APISIX路由创建失败Admin API请求格式错误或插件未启用。1. 检查JSON格式是否正确特别是正则表达式中的引号转义。2. 确认使用的插件如response-rewrite已在config.yaml中启用。3. 使用curl -v输出详细请求/响应信息查看APISIX返回的错误消息。6.2 Nginx调试技巧实录当配置复杂时echo模块是你的好朋友。但生产环境通常不装。我们可以用return和日志来调试。# 临时调试配置 location ~* ^/actuator { # 记录原始请求URI和规范化后的URI到独立日志 access_log /tmp/nginx_debug.log main; set $debug_info req_uri:$request_uri, uri:$uri; # 你可以通过返回200并输出变量值来检查仅限调试 # return 200 $debug_info\n; deny all; return 403; }查看/tmp/nginx_debug.log你可以清晰地看到$request_uri原始和$uri规范化后的区别从而判断匹配逻辑。6.3 APISIX调试技巧实录APISIX提供了强大的debug插件和Admin API来检查路由匹配。使用debug插件为你的拦截路由临时加上debug插件。plugins: { response-rewrite: { ... }, debug: { log_level: info, hook: rewrite } }这会在APISIX的error.log中输出详细的插件执行和变量信息。通过Admin API检查路由curl http://ADMIN_IP:PORT/apisix/admin/routes/block-actuator -H X-API-KEY: KEY确认配置是否按预期生效。使用batch-requests插件测试这是一个高级技巧可以模拟多个请求一次性测试各种绕过Payload。6.4 性能影响评估添加复杂的正则匹配和if判断是否会显著影响性能对于管理端点/actuator影响微乎其微因为这些端点的访问频率极低可能每分钟只有几次健康检查。额外的正则匹配开销在整体流量中占比可以忽略。优化建议如果确实担心可以将最精确、最常用的匹配如location /actuator放在前面它匹配速度快。复杂的正则匹配放在稍后位置。在APISIX中确保拦截路由的priority高但URI匹配规则本身是高效的。最后安全是一个持续的过程而不是一次性的配置。字符绕过漏洞提醒我们对于安全规则必须抱有“零信任”的态度从请求的原始字节开始思考通过多层、深度的防御来构建真正可靠的安全体系。每次配置变更后养成用多种攻击向量测试的习惯这比任何复杂的理论都更可靠。
Spring Boot Actuator安全防护:Nginx与APISIX字符绕过漏洞深度解析与配置实践
发布时间:2026/7/1 4:57:47
1. 项目概述从一次紧急告警说起那天晚上我正在处理一个线上服务的性能调优突然监控告警响了不是CPU飙升也不是内存泄漏而是一条安全扫描的告警“疑似存在Actuator端点未授权访问风险”。我心里咯噔一下Actuator这玩意儿我熟啊Spring Boot的监控利器但要是暴露在外网那简直就是给黑客开了个后门。我赶紧登录服务器检查发现Nginx的配置里确实有对/actuator路径的代理但配置了deny all。奇怪按道理应该防住了。我用curl随手试了几个变形路径比如/actuator/、/actuator..;/、/actuator%2f结果当请求/actuator%2fhealth时服务竟然返回了健康状态信息问题找到了这就是典型的“字符绕过漏洞”。攻击者通过构造特殊的字符如/的URL编码%2f或;、..等可以绕过Web服务器如Nginx或API网关如Apache APISIX中基于路径前缀匹配的安全规则直接访问到本应被禁止的后端敏感接口。这个项目就是围绕如何彻底堵上这个安全漏洞展开的。它不仅仅是加一条deny指令那么简单而是需要深入理解Nginx和APISIX的配置语法、请求处理流程以及不同字符在HTTP请求解析中的微妙差异。无论是运维工程师、开发人员还是安全负责人只要你负责的线上服务用到了Spring Boot Actuator并通过Nginx或APISIX对外暴露这篇文章里详尽的配置方案和避坑经验都能帮你构建起更坚固的第一道防线。我们将从漏洞原理拆解开始一步步深入到Nginx和APISIX的具体、可落地的安全配置实践中。2. 漏洞核心原理与风险场景拆解2.1 Actuator端点为何成为靶子Spring Boot Actuator提供了一系列生产就绪的特性主要用于监控和管理应用。它暴露的端点如/actuator/health健康检查、/actuator/env环境变量、/actuator/metrics指标甚至/actuator/heapdump堆转储对于运维来说是无价之宝。然而这些端点如果未经保护直接暴露风险极高。/actuator/env可能泄露数据库密码、API密钥/actuator/heapdump可能包含内存中的敏感数据/actuator/loggers甚至可以动态修改日志级别干扰系统运行。因此最佳实践是在应用层通过Spring Security进行严格的认证和授权。但现实中很多团队会选择在网关或反向代理层进行“一刀切”的屏蔽认为这样更简单、对应用无侵入。正是这种“图省事”的思路遇到了路径解析的复杂性才催生了字符绕过漏洞。2.2 字符绕过的“花式”手法字符绕过漏洞的本质是配置的匹配规则与Web服务器/网关的实际解析规则不一致。攻击者利用的是这种“认知差”。以下是一些常见的绕过手法斜杠编码绕过这是最常见的一种。Nginx配置中location /actuator { deny all; }其意图是匹配以/actuator开头的路径。但攻击者请求/actuator%2fhealth。这里的%2f是/的URL编码。在某些默认配置下Nginx在匹配location时可能不会自动解码%2f因此不认为该请求以/actuator开头从而绕过规则。而后端的Tomcat或Netty服务器在收到请求时通常会进行URL解码将%2f还原为/于是请求就被正确路由到了/actuator/health端点。路径回溯绕过利用..进行路径回溯。例如请求/actuator/../health。经过规范化normalization后这个路径等价于/health。如果Nginx的配置是简单地匹配/actuator前缀然后deny那么这个请求在Nginx看来是/health不被拦截。但有些后端服务或特定的配置下可能不会进行严格的路径规范化导致异常解析。分号参数绕过在路径中插入分号;。例如/actuator;/health。根据旧的HTTP规范和某些服务器的实现;有时会被视为路径参数path parameters的分隔符路径部分在;之前。Nginx的location匹配可能只看到/actuator而Java应用服务器可能会将/actuator;/health解析为访问/actuator/health。多重编码与大小写混淆更复杂的攻击会使用双重URL编码如%252f代表%2f再解码为/或者混合大小写如/AcTuAtOr试图绕过一些不够严谨的正则表达式匹配。注意并非所有Nginx或APISIX的版本和配置都会存在这些绕过问题。漏洞是否成立高度依赖于具体的配置指令、版本以及后端应用服务器的行为。我们的目标是通过配置消除这些不确定性实现无论攻击者如何变形都能精准拦截。2.3 影响范围与严重性评估这个漏洞的影响是直接的导致敏感的Actuator管理接口暴露在公网。其严重性取决于暴露的端点类型高风险暴露env,heapdump,trace,logfile等端点可能导致直接的信息泄露甚至远程代码执行如果结合其他漏洞。中风险暴露health,info,metrics等端点可能泄露系统内部状态、组件版本信息为后续攻击提供情报。低风险仅暴露health端点且不包含详情风险相对较低但仍会暴露服务存活状态。即使你认为Actuator端点已在内网只要流量经过Nginx/APISIX且配置存在瑕疵来自内部的横向移动攻击同样可能利用此漏洞。3. Nginx 安全配置实战与深度解析Nginx的配置灵活且强大但“能力越大责任越大”不严谨的配置就会留下缝隙。下面我们构建一个从简单到坚固的多层防御配置。3.1 基础但脆弱的配置为何deny all会失效很多人的第一版配置可能是这样的server { listen 80; server_name your-domain.com; location /actuator { deny all; return 403; # 显式返回403 } location / { proxy_pass http://backend-service; # ... 其他代理设置 } }这个配置意图很明确拦截所有访问/actuator的请求。但它为什么会被/actuator%2fhealth绕过呢关键在于location的匹配过程。默认情况下location /actuator进行的是前缀匹配。对于请求/actuator%2fhealthNginx在匹配location时看到的路径字符串字面就是/actuator%2fhealth。它以/actuator开头吗是的从字符串上看/actuator%2fhealth确实以/actuator开头。所以这个配置其实能拦截/actuator%2fhealth我之前的测试结果需要修正更常见的绕过场景是下面这种配置location ~ ^/actuator(/|$) { deny all; }或者当location指令中使用了一些不恰当的修饰符或嵌套时问题才会显现。但为了绝对安全我们不能依赖默认行为需要更精确的武器。3.2 强化配置方案一精确正则表达式匹配使用正则表达式location可以更精确地控制匹配逻辑避免解码歧义。server { listen 80; server_name your-domain.com; # 方案1使用正则表达式匹配拒绝所有包含 /actuator/ 的请求 location ~* ^/actuator(/|$) { deny all; return 403; # 记录日志便于审计 access_log /var/log/nginx/actuator_block.log; } # 方案1的变种更严格匹配 /actuator 后跟任意非空路径 location ~* ^/actuator/(.*)$ { deny all; return 403; } location / { proxy_pass http://backend-service; proxy_set_header Host $host; # 关键传递原始请求URI防止Nginx规范化导致绕过 proxy_set_header X-Original-URI $request_uri; } }配置解析与避坑指南location ~* ^/actuator(/|$)~*表示不区分大小写的正则匹配。^匹配开头(/|$)表示后面要么接一个斜杠要么直接结束即匹配/actuator和/actuator/*。这个配置能有效拦截/actuator、/actuator/、/actuator/health以及/actuator%2fhealth因为正则匹配是在Nginx进行了一定程度的解码之后进行的这里需要澄清。一个重要陷阱Nginx在处理正则表达式location匹配时使用的$uri变量是已经经过解码、规范化后的URI。也就是说对于请求/actuator%2fhealth$uri的值会是/actuator/health。因此上面的正则配置能够成功匹配并拦截。这反而是个好事proxy_set_header X-Original-URI $request_uri;这是一个防御性技巧。$request_uri是原始的、未经解码的请求URI。将其传递给后端后端应用可以记录或检查原始请求用于更高级的安全审计或二次校验。实操心得正则表达式location虽然强大但性能上略逊于前缀匹配。如果访问量极大需要评估影响。不过对于/actuator这类管理端点访问频率极低性能开销可忽略不计。3.3 强化配置方案二利用map指令实现集中化管理当你有多个server块或需要管理大量敏感路径时在每个location里写deny all会显得冗余。使用map指令可以将黑名单逻辑抽象出来使配置更清晰、更易维护。# 在http块中定义映射置于所有server块之外 http { # 定义一个变量 $block_actuator根据$uri进行映射 map $uri $block_actuator { default 0; # 使用正则表达式匹配需要拦截的路径 ~*^/actuator(/|$) 1; # 你可以在这里轻松添加其他敏感路径 ~*^/admin(/|$) 1; ~*^/phpmyadmin(/|$) 1; } server { listen 80; server_name your-domain.com; # 在进入主要location前进行拦截 if ($block_actuator) { deny all; return 403; } location / { proxy_pass http://backend-service; } } }配置解析与避坑指南map $uri $block_actuator创建一个新变量$block_actuator。它遍历$uri如果匹配到右侧的任何一条正则规则则值为1否则为默认值0。if ($block_actuator)谨慎使用if。在Nginx中if是“邪恶的”但在server上下文中用于简单的变量判断和return/deny通常是安全的。这里的作用是如果$block_actuator为1则执行拦截。优势管理方便。增加新的拦截路径只需在map块中添加一行规则。配置逻辑集中一目了然。注意map指令使用的也是规范化后的$uri因此同样能防御编码绕过。3.4 终极加固方案结合location优先级与错误处理最严谨的做法是结合多种手段并考虑边缘情况。server { listen 80; server_name your-domain.com; # 首先显式拒绝所有对 /actuator 的访问包括各种变形 # 使用 进行精确匹配最高优先级 location /actuator { deny all; return 403; } # 使用正则匹配所有 /actuator/ 下的请求 location ~ ^/actuator/(.*)$ { deny all; return 403; } # 其次设置一个兜底的默认拦截处理可能的畸形请求 # 这个location匹配所有请求优先级最低 location / { # 在代理前再次使用if检查原始请求URI$request_uri # 这是一个更保险但性能开销稍大的检查 if ($request_uri ~* ^/[Aa][Cc][Tt][Uu][Aa][Tt][Oo][Rr]) { deny all; return 403; } # 正常的代理设置 proxy_pass http://backend-service; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 强烈建议传递原始URI供后端审计 proxy_set_header X-Original-URI $request_uri; # 隐藏后端服务器头信息增加攻击者难度 proxy_hide_header X-Powered-By; proxy_hide_header Server; } # 最后自定义错误页面避免暴露Nginx版本信息 error_page 403 /403.html; location /403.html { root /usr/share/nginx/html; internal; # 防止直接访问 } }配置深度解析优先级策略Nginx的location优先级为精确匹配 ^~非正则前缀匹配 ~或~*正则匹配 /通用前缀匹配。我们利用最高优先级确保/actuator被精确拦截。双重校验在通用的location /中我们使用if ($request_uri ~* “…”)对原始请求URI$request_uri进行二次正则匹配。$request_uri包含原始查询字符串且未经规范化可以捕捉到一些极其特殊的绕过尝试。注意频繁使用if进行正则匹配对性能有影响需权衡。对于管理端点通常可接受。信息隐藏使用proxy_hide_header移除后端应用如Spring Boot返回的X-Application-Context等头信息这些信息可能暗示Actuator的存在。错误页面自定义403页面避免使用默认页面因为默认页面可能包含Nginx版本为攻击者提供信息。4. Apache APISIX 安全配置实战与策略Apache APISIX作为一个动态、实时、高性能的API网关其配置方式与Nginx静态文件不同主要通过Admin API动态管理。其防护思路类似但实现工具是路由Route和插件Plugin。4.1 理解APISIX的路由匹配机制APISIX中一个请求由哪个“上游”Upstream处理由路由Route的匹配规则决定。路由可以基于URI、Host、Method、Header等多种条件进行匹配。我们的目标是创建一条规则匹配所有试图访问Actuator端口的请求并拒绝它们。APISIX的URI匹配支持两种模式前缀匹配/actuator*。类似于Nginx的前缀匹配但同样可能受到编码问题影响。正则匹配更强大和精确是我们防御的主力。4.2 配置方案使用uri正则匹配与response-rewrite插件最直接的方式是创建一个匹配Actuator路径的路由并配置一个插件直接返回403。通过APISIX Admin API配置curl -X PUT http://APISIX_ADMIN_IP:PORT/apisix/admin/routes/block-actuator \ -H X-API-KEY: YOUR_ADMIN_KEY \ -d { name: block-springboot-actuator, desc: 拦截所有对Spring Boot Actuator端口的访问防止字符绕过, uri: [~* ^/actuator(/|$), ~* ^/actuator%2f.*$], // 关键同时匹配规范化和原始编码路径 plugins: { response-rewrite: { vars: [ [status, , 403] ], body: Access Denied., status_code: 403 } }, upstream: { type: roundrobin, nodes: { 127.0.0.1: 9999 // 可以指向一个不存在的或专门返回错误的上游 } }, priority: 9999 // 设置高优先级确保优先匹配 }配置解析uri: [~* ^/actuator(/|$), ~* ^/actuator%2f.*$]这是防御的核心。我们定义了两个正则条件只要满足其一即匹配。~* ^/actuator(/|$)匹配规范化后的路径如/actuator、/actuator/health。~* ^/actuator%2f.*$直接匹配包含原始编码%2f的请求路径。这是应对Nginx中可能出现的、在匹配阶段未解码情况的强力手段。APISIX的路由匹配逻辑可能允许这样的原始字符串匹配。plugins: { response-rewrite: ... }使用response-rewrite插件。当匹配到该路由时不将请求转发给上游而是直接改写响应返回403状态码和自定义内容。priority: 9999优先级数字越大优先级越高。确保这条拦截路由在匹配到/actuator时优先于其他可能代理到真实后端服务的路由。4.3 进阶方案使用uri-blocker插件或自定义插件除了response-rewriteAPISIX社区还有更专门的插件。方案A使用uri-blocker插件如果已安装或自定义开发uri-blocker插件可以基于URI进行阻断配置更简洁。{ uri: /actuator*, // 前缀匹配作为初始过滤 plugins: { uri-blocker: { block_rules: [^/actuator[/\\s\\S]*, ^.*%2f.*actuator.*$], // 强大的正则阻断规则 rejected_code: 403 } }, upstream: { ... } }block_rules中的正则表达式可以设计得非常严格例如第二条规则^.*%2f.*actuator.*$会阻断任何URI中包含%2f和actuator字样的请求无论顺序防御范围更广。方案B结合proxy-rewrite插件和“黑洞”上游创建一个返回403的独立服务例如一个简单的HTTP服务器或者使用一个无效的上游地址。当匹配到拦截路由时将请求proxy-rewrite到“黑洞”上游。{ uri: [~* ^/actuator], plugins: { proxy-rewrite: { uri: /blocked, // 重写URI到黑洞上游的特定路径 scheme: http, host: 127.0.0.1, port: 8888 // 假设8888端口运行着一个只返回403的服务 } } }4.4 APISIX配置的注意事项与最佳实践优先级管理APISIX的路由匹配顺序受优先级priority和创建时间影响。务必为你所有的拦截路由设置足够高的优先级确保它们在可能冲突的代理路由之前被匹配。测试覆盖配置完成后务必使用各种绕过手法进行测试。curl -v http://APISIX_GATEWAY_IP:PORT/actuator/health curl -v http://APISIX_GATEWAY_IP:PORT/actuator%2fhealth curl -v http://APISIX_GATEWAY_IP:PORT/actuator;/health curl -v http://APISIX_GATEWAY_IP:PORT/AcTuAtOr/health所有请求都应返回403而不是后端服务的响应。动态生效APISIX的配置是动态生效的无需重启。这既是优点也是风险。确保Admin API的访问密钥X-API-KEY得到严格保护。日志与监控为拦截路由启用访问日志并监控其触发频率。异常的频繁拦截可能意味着正在遭受扫描或攻击。5. 通用防御策略与架构思考在网关层配置拦截是重要的防线但并非万无一失。真正的安全需要多层防御Defense in Depth。5.1 应用层加固不可或缺的最后防线网关的规则可能被绕过例如通过未知的编码变形、网关的bug或配置错误因此应用层必须有自己的防护。使用Spring Security保护Actuator端点这是官方推荐且最根本的方式。你可以轻松地集成HTTP Basic认证、OAuth2、JWT等。# application.yml management: endpoints: web: exposure: include: health,info # 仅暴露必要的端点 base-path: /manage # 修改默认的/actuator路径增加隐蔽性 endpoint: health: show-details: when_authorized # 健康检查详情仅对授权用户显示// Security配置类 Configuration public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/manage/health).permitAll() // 健康检查可公开 .antMatchers(/manage/**).hasRole(ACTUATOR_ADMIN) // 其他端点需要特定角色 .and() .httpBasic(); // 使用HTTP Basic认证 } }禁用不必要的端点在生产环境中只暴露真正需要的端点如health用于负载均衡检查。将Actuator端口与业务端口分离通过management.server.port配置让Actuator运行在一个独立的、仅内网可访问的端口上。这是最彻底的隔离方案。5.2 网络层隔离最物理的屏障安全组/防火墙规则在云服务器或防火墙层面严格限制对Actuator端口默认8080或自定义的管理端口的访问只允许特定的运维IP或跳板机访问。内部负载均衡器将包含Actuator的内部端口只暴露给内部的负载均衡器或服务网格如Istio对外只暴露业务端口。5.3 持续监控与响应日志聚合与分析收集Nginx/APISIX的访问日志和应用日志设置告警规则。例如监控对/actuator路径的任何访问包括403请求超过阈值即告警。定期漏洞扫描与渗透测试使用自动化工具如Nessus, Qualys, 或开源的nmap,dirb等定期对公网入口进行扫描模拟攻击者的字符绕过尝试检验配置的有效性。配置即代码与自动化审计将Nginx/APISIX的配置文件纳入版本管理如Git。通过CI/CD流水线在每次变更前进行静态分析例如使用nginx -t测试语法使用自定义脚本检查是否存在不安全的location规则确保安全配置不被意外修改或破坏。6. 常见问题排查与实战调试记录即使配置看起来完美在实际部署中也可能遇到意外。以下是我在多次实战中总结的问题和排查思路。6.1 配置不生效按步骤排查问题现象可能原因排查步骤访问/actuator返回404而非403拦截规则优先级低于其他路由请求被代理到了后端而后端没有该路径。1. 检查Nginx中location块的优先级顺序。确保拦截用的location尤其是精确匹配或正则匹配~定义在通用代理规则location /之前。2. 在APISIX中检查路由的priority字段确保拦截路由的优先级最高。某些编码绕过仍然成功匹配规则不够全面或使用的变量$urivs$request_uri不对。1. 在Nginx配置中增加对$request_uri的检查如方案三中的if语句。2. 在APISIX中在uri匹配规则中显式添加对编码字符的匹配如~* ^/actuator%2f。3.开启调试日志在Nginx中设置error_log logs/error.log debug;观察请求处理流程。返回403但日志里没有记录日志路径配置错误或日志缓冲区未刷新。1. 检查access_log指令的路径和权限。2. 使用tail -f命令实时跟踪日志文件。3. 确认deny all;指令后是否有access_log指令它可能不会被执行。可以将日志记录移到server层级。APISIX路由创建失败Admin API请求格式错误或插件未启用。1. 检查JSON格式是否正确特别是正则表达式中的引号转义。2. 确认使用的插件如response-rewrite已在config.yaml中启用。3. 使用curl -v输出详细请求/响应信息查看APISIX返回的错误消息。6.2 Nginx调试技巧实录当配置复杂时echo模块是你的好朋友。但生产环境通常不装。我们可以用return和日志来调试。# 临时调试配置 location ~* ^/actuator { # 记录原始请求URI和规范化后的URI到独立日志 access_log /tmp/nginx_debug.log main; set $debug_info req_uri:$request_uri, uri:$uri; # 你可以通过返回200并输出变量值来检查仅限调试 # return 200 $debug_info\n; deny all; return 403; }查看/tmp/nginx_debug.log你可以清晰地看到$request_uri原始和$uri规范化后的区别从而判断匹配逻辑。6.3 APISIX调试技巧实录APISIX提供了强大的debug插件和Admin API来检查路由匹配。使用debug插件为你的拦截路由临时加上debug插件。plugins: { response-rewrite: { ... }, debug: { log_level: info, hook: rewrite } }这会在APISIX的error.log中输出详细的插件执行和变量信息。通过Admin API检查路由curl http://ADMIN_IP:PORT/apisix/admin/routes/block-actuator -H X-API-KEY: KEY确认配置是否按预期生效。使用batch-requests插件测试这是一个高级技巧可以模拟多个请求一次性测试各种绕过Payload。6.4 性能影响评估添加复杂的正则匹配和if判断是否会显著影响性能对于管理端点/actuator影响微乎其微因为这些端点的访问频率极低可能每分钟只有几次健康检查。额外的正则匹配开销在整体流量中占比可以忽略。优化建议如果确实担心可以将最精确、最常用的匹配如location /actuator放在前面它匹配速度快。复杂的正则匹配放在稍后位置。在APISIX中确保拦截路由的priority高但URI匹配规则本身是高效的。最后安全是一个持续的过程而不是一次性的配置。字符绕过漏洞提醒我们对于安全规则必须抱有“零信任”的态度从请求的原始字节开始思考通过多层、深度的防御来构建真正可靠的安全体系。每次配置变更后养成用多种攻击向量测试的习惯这比任何复杂的理论都更可靠。