Azure APIM配置IP阻止策略无效,如何拦截恶意扫描请求?
排查Azure APIM拦截恶意扫描请求的策略失效问题
问题描述
我需要拦截针对网站的扫描请求(比如路径以/.env结尾的请求),请求流转路径为:Azure FD → Node.js服务器 → APIM → 后端服务器。我在Azure API管理(APIM)的产品级别配置了策略,意图缓存恶意扫描IP并阻止其访问,但策略未生效,/.env这类请求仍能正常通过。已确认产品级别的速率限制策略可以生效,但恶意请求拦截逻辑无效,请帮忙排查配置错误并提供可行方案。
当前策略代码
<policies> <inbound> <base /> <!-- Check if this IP is blocked --> <cache-lookup-value key="@("blocked-ip-" + context.Request.IpAddress)" variable-name="isBlocked" /> <choose> <when condition="@(context.Variables.ContainsKey("isBlocked"))"> <return-response> <set-status code="403" reason="Forbidden" /> <set-body>Access denied.</set-body> </return-response> </when> </choose> <!-- Detect /.env probe and block the IP --> <choose> <when condition="@(context.Request.Url.Path.EndsWith("/.env") || context.Request.Url.Path.EndsWith("/.env."))"> <cache-store-value key="@("blocked-ip-" + context.Request.IpAddress)" value="true" duration="300" /> <return-response> <set-status code="403" reason="Forbidden" /> <set-body>Access denied.</set-body> </return-response> </when> </choose> <!-- This is just a test policy to see it's being reached. It IS being reached. --> <rate-limit-by-key calls="1" renewal-period="6" counter-key="@(context.Request.IpAddress)" /> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
问题根源排查
真实客户端IP获取错误
请求经过Azure FD和Node.js转发后,context.Request.IpAddress获取的是Node.js服务器的IP,而非攻击者的真实IP。APIM默认取直接连接的客户端IP,导致你封禁的是中间转发服务器,而非真正的扫描来源。路径匹配逻辑局限性
EndsWith判断仅能匹配严格以/.env或/.env.结尾的路径,但恶意扫描可能携带参数(如/.env?test=1)、URL编码(如/.env%20)或多余斜杠(如/.env/),此时匹配逻辑会失效。缓存读取/存储的一致性风险
若APIM缓存配置未正确启用,或产品级策略使用的缓存空间与预期不符,可能导致封禁记录无法正常读取,无法触发拦截。
修复后的策略方案
首先确保Node.js服务器转发请求时添加X-Forwarded-For头传递真实客户端IP,然后修改APIM策略如下:
<policies> <inbound> <!-- 获取真实客户端IP:优先读取X-Forwarded-For的第一个IP --> <set-variable name="clientIp" value="@(context.Request.Headers.ContainsKey("X-Forwarded-For") ? context.Request.Headers["X-Forwarded-For"][0].Split(',')[0].Trim() : context.Request.IpAddress)" /> <!-- 检查IP是否已被封禁 --> <cache-lookup-value key="@("blocked-ip-" + context.Variables["clientIp"])" variable-name="isBlocked" /> <choose> <when condition="@(context.Variables.ContainsKey("isBlocked"))"> <return-response> <set-status code="403" reason="Forbidden" /> <set-body>Access denied.</set-body> </return-response> </when> </choose> <!-- 扩展路径匹配:检测任何包含/.env的路径(忽略大小写) --> <choose> <when condition="@(context.Request.Url.Path.ToLower().Contains("/.env") || context.Request.Url.RawPath.ToLower().Contains("/.env"))"> <cache-store-value key="@("blocked-ip-" + context.Variables["clientIp"])" value="true" duration="300" /> <return-response> <set-status code="403" reason="Forbidden" /> <set-body>Access denied.</set-body> </return-response> </when> </choose> <!-- 测试用速率限制(已适配真实客户端IP) --> <rate-limit-by-key calls="1" renewal-period="6" counter-key="@(context.Variables["clientIp"])" /> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
额外优化建议
- 前端拦截前置:直接在Azure Front Door配置规则,拦截包含
/.env、/admin等恶意路径的请求,减少后续节点的压力。 - 正则匹配增强:使用正则表达式覆盖更多扫描场景,比如:
@(Regex.IsMatch(context.Request.Url.Path, @"\.env(\.|$|\?)", RegexOptions.IgnoreCase)) - IP列表补充防护:将频繁扫描的IP直接添加到APIM的IP拒绝列表,配合缓存封禁实现多层防护。
- 日志验证:启用APIM请求日志,查看
clientIp、Request.Url.Path等变量的实际值,确认匹配逻辑是否触发。
内容的提问来源于stack exchange,提问作者PythonForEver
相关产品推荐
相关产品推荐

