AWS API Gateway尾部斜杠绕过授权:原理、复现与修复

发布时间:2026/8/28 11:06:43
AWS API Gateway尾部斜杠绕过授权:原理、复现与修复 从标题说起为什么一个斜杠值得写一整篇文章看到这个标题第一反应可能觉得是“配置失误导致的偶然漏洞”。但如果你把它拆开看会发现很多团队在 AWS API Gateway 上做授权保护时都存在同一个思维盲区路径匹配是一套逻辑授权评估是另一套逻辑而它们对同一个 URL 的“理解方式”并不完全一致。攻击者只要在这个夹缝里做一点手脚比如在请求路径末尾补一个/就可能让 API Gateway 以为这是另一个资源从而绕过原本配置好的授权器。这类问题不是只在 AWS 上存在凡是基于路径路由的网关、负载均衡、反向代理都可能出现类似语义差异。但在 API Gateway 场景里它有一个特别危险的特点管理员通常在控制台里看得清清楚楚——“/admin 资源已经启用了 Lambda Authorizer没问题”然后实际请求/admin/却走了另一条没有校验的路径到达同一个后端函数。这篇文章要讲清楚三件事尾部斜杠为什么会绕过 API Gateway 的授权如何在自己的环境里复现并确认这个问题生产环境应该用哪些手段把它堵住。读完后你可以拿着文中的配置思路去检查自己的 API 设计避免把“看起来安全”的接口暴露在公网上。1. 这篇文章真正要解决的问题很多团队在 API Gateway 上做过权限控制通常的流程是创建资源、绑定方法、关联 Lambda Authorizer 或 Cognito JWT 授权器然后用 Postman 或 curl 验几个正常请求感觉没问题就发布了。这个流程里隐藏着一个关键盲区——你只验证了那些“你认为应该被保护”的路径但没有验证“形态相似但可能绕过保护”的路径。具体来说当一个 API 同时存在以下两种资源时问题就来了/admin管理后台入口配置了严格的授权校验/admin/{proxy}给后台内部 API 用的通配转发可能没有配置授权器或者配置了但策略逻辑不严谨。正常情况下客户端请求/adminAPI Gateway 会把请求路由到/admin资源先经过授权器再进入后端。但如果客户端故意请求/admin/末尾多一个斜杠API Gateway 在路径匹配时会把它归入/admin/{proxy}这一类资源因为/admin/包含了“额外路径片段”。此时如果/admin/{proxy}没有授权器保护那么请求就畅通无阻地到达后端。这就是一个典型配置级绕过。它带来的影响不是“某个静态文件能访问”而是管理员权限下的业务操作可能被未授权用户触发。如果后端函数没有做二次鉴权攻击者可以直接读取用户列表、修改配置、导出数据甚至执行删除操作。这篇文章适合以下读者负责 AWS API Gateway 配置和运维的工程师用 Lambda Authorizer 或 JWT Authorizer 做接口鉴权的后端开发者对 Web 安全、API 安全、认证绕过感兴趣的安全测试人员。2. 基础概念API Gateway 的授权链路与路径匹配先梳理一下 API Gateway 处理一次请求时经历了哪些环节。以 REST API 为例一个请求到达后主要经过三个阶段路由匹配根据请求方法GET/POST 等和请求路径找到对应的资源与集成配置授权评估如果该资源绑定了授权器API Gateway 会在调用后端集成之前执行授权器逻辑根据返回的策略决定放行还是拒绝后端集成授权通过后API Gateway 将请求转发给 Lambda、HTTP 后端或其他 AWS 服务。绕过的核心就在这里路由匹配和授权评估是两个独立的决策过程。路由匹配决定“这个请求归哪个资源处理”授权评估决定“这个请求能不能被该资源处理”。如果攻击者通过构造路径让路由匹配落到一个“没有授权器”的资源上授权评估这一环节就被直接跳过。再来看路径匹配。API Gateway 的资源路径是以/分隔的路径段模型。假设你定义了以下资源结构/ ├── admin │ └── {proxy} └── users此时/admin是一个精确路径段资源/admin/{proxy}是一个通配符资源。API Gateway 的匹配规则大致是先尝试精确匹配再尝试通配匹配。当请求路径是/admin/时它由两段组成第一段是admin第二段是空字符串。这个空字符串不会让请求等于/admin而是会被通配符{proxy}捕获。理解这一点后你会立刻意识到如果你在/admin上做了授权但在/admin/{proxy}上没有做同样的授权那么所有带有尾部斜杠的管理请求都不会经过鉴权。另一个需要注意的概念是“授权器”。API Gateway 支持三种常见的授权方式授权方式说明常见使用场景Cognito User Pools直接集成 Amazon Cognito 用户池校验令牌面向最终用户的移动 App、Web 应用Lambda Authorizer用自定义 Lambda 函数判断令牌或上下文需要复杂策略、多条件判断、动态权限的接口JWT AuthorizerHTTP API内置 JWT 校验验证 iss、aud、签名快速接入第三方身份提供商其中 Lambda Authorizer 最容易出现路径理解不一致的问题因为授权逻辑是开发者自己写的它看到的路径是event.path而 API Gateway 内部生成策略时使用的是methodArn。如果开发者只对某一个精确路径返回 Allow其他请求默认返回 Deny但同时写了“请求路径不是/admin就放行”这种漏洞逻辑也会造成绕过。这部分在后面的示例里会详细演示。3. 漏洞原理为什么尾部斜杠能绕过授权现在把关键问题单独拿出来分析一个尾部斜杠到底如何影响整个链路我们从请求的解析差异说起。3.1 两种“路径理解”客户端视角/admin和/admin/在很多语义下可以视为同一个页面尤其在后端框架里Django、Flask、Spring 等都会自动处理末尾斜杠把/admin/重定向或归一化到/admin。API Gateway 视角/admin和/admin/是两个不同的路径表达式因为 API Gateway 的资源路径是按照“分段”来匹配的末尾多一个空段意味着路径结构不同。正是这种差异让攻击者有机会把请求导流到另一条路由规则上。3.2 两种常见的绕过形态形态一资源配置遗漏这是最直观的。API 设计者维护了这样一个结构/admin → GET → Lambda Authorizer允许 /admin/{proxy} → GET → 直接集成后端 Lambda无授权器攻击者发送GET /admin/ HTTP/1.1 Host: api.example.comAPI Gateway 会匹配/admin/{proxy}因为/admin/有额外的路径段。如果这个资源没有绑定授权器网关直接把它转发给后端 Lambda绕过了/admin上的保护。形态二Lambda Authorizer 策略逻辑缺陷即使/admin/{proxy}绑定了同一个 Lambda Authorizer问题也可能出在授权函数本身。例如def lambda_handler(event, context): # 只对精确路径 /admin 放行 if event[path] /admin: return generate_allow_policy(event[methodArn]) # 缺失 else 分支或 else 分支被错误写成 Allow return generate_allow_policy(event[methodArn])当请求路径是/admin/时event[path] ! /admin但代码没有走 Deny而是默认返回了 Allow 策略API Gateway 就会放行。更隐蔽的是有些实现会用path.startswith(/admin)做判断本意是保护所有/admin开头的路径但/admin/.startswith(/admin)也为 True这种反而安全。真正危险的是只判断“等于某个精确值”或者忘记了最后必须明确返回 Deny。3.3 授权评估的时机问题还有一个容易被忽略的点Lambda Authorizer 的缓存机制。API Gateway 会根据methodArn缓存授权策略也就是说同一个资源方法路径在缓存时间内只执行一次授权器。如果一台客户端请求/admin时触发了授权器返回了 Allow随后攻击者请求/admin/很可能匹配到的是完全不同的methodArn这就会产生新的授权调用。这本身不算漏洞但它意味着不要指望某一次授权检查能覆盖另一个路径形态的请求。4. 复现环境准备在开始复现之前需要准备以下环境一个 AWS 账号建议使用测试账号或开发环境不要直接在正式环境操作AWS CLI 已安装并配置好凭证基本权限创建 IAM 角色、Lambda 函数、API Gateway API、CloudWatch 日志组熟悉 Linux/macOS 终端操作Windows 用户可以借助 Git Bash 或 WSL。本文示例统一使用us-east-1区域实际使用时请替换为自己的区域。为了便于描述下文中的账号 ID 使用123456789012占位实际环境请使用自己账号的 ID。需要提醒的是安全测试类实验必须在自有环境或得到授权的前提下进行不要对生产系统做任何未授权的验证操作。5. 完整复现示例从零搭建一个可被绕过的 API下面通过 OpenAPI 规范、Lambda 授权器、后端函数三个部分来构建一个最小复现环境。整个过程可以在 AWS 控制台完成也可以使用 CloudFormation 或 SAM。这里使用 AWS CLI OpenAPI 文件的方式方便你看到完整配置。5.1 创建后端 Lambda 函数后端函数不区分请求来源直接返回“管理后台数据”。实际业务中这里可能是用户管理、订单管理、系统配置等敏感操作。# 文件路径lambda-backend/index.py import json def lambda_handler(event, context): # 在 API Gateway 代理集成模式下event 中包含请求路径等信息 path event.get(rawPath, event.get(path, /)) return { statusCode: 200, headers: {Content-Type: application/json}, body: json.dumps({ message: admin data leaked, path: path, level: secret }) }部署步骤通常是把这段代码打包成 zip 后上传cd lambda-backend zip -r ../backend.zip index.py aws lambda create-function \ --function-name admin-backend \ --runtime python3.12 \ --role arn:aws:iam::123456789012:role/lambda-basic-role \ --handler index.lambda_handler \ --zip-file fileb://../backend.zip创建完成后记录函数的 ARN在 OpenAPI 文件里会用到。5.2 创建带缺陷的 Lambda Authorizer这个授权器模拟了“看起来保护了 /admin但实际上没有保护 /admin/”的缺陷。它只对精确路径/admin返回 Allow对/admin/返回 404 错误而不是显式 Deny。# 文件路径lambda-authorizer/index.py import json def generate_policy(principal_id, effect, resource): return { principalId: principal_id, policyDocument: { Version: 2012-10-17, Statement: [ { Action: execute-api:Invoke, Effect: effect, Resource: resource } ] } } def lambda_handler(event, context): # API Gateway 传入的 methodArn 形如 # arn:aws:execute-api:us-east-1:123456789012:api-id/stage/GET/admin method_arn event[methodArn] path event.get(path, /) print(request path:, path) print(method arn:, method_arn) # 缺陷点只精确匹配 /admin if path /admin: return generate_policy(admin-user, Allow, method_arn) else: # 这里直接返回 Deny 才是安全做法 return generate_policy(admin-user, Deny, method_arn)注意这段代码已经是“修复前”的安全对比版本。真正的缺陷版通常长这样if path /admin: return generate_policy(admin-user, Allow, method_arn) # 忘了写 else或者 else 分支返回 allow return generate_policy(admin-user, Deny, method_arn) # 如果写成 Allow 就是漏洞在文章示例里我采用“返回 Deny”的写法同时补充说明如果写成 Allow 就是漏洞。这样读者既能看清问题也能安全地复现。部署cd lambda-authorizer zip -r ../authorizer.zip index.py aws lambda create-function \ --function-name admin-authorizer \ --runtime python3.12 \ --role arn:aws:iam::123456789012:role/lambda-basic-role \ --handler index.lambda_handler \ --zip-file fileb://../authorizer.zip5.3 创建 API Gateway 并绑定授权器下面用 OpenAPI 文件来定义 API 结构。核心配置是/admin这个路径的方法启用了自定义授权器AdminAuthorizer/admin/{proxy}这个路径的方法没有配置授权器直接集成到同一个后端。# 文件路径api-definition.yaml openapi: 3.0.1 info: title: TrailingSlashDemo version: 1.0.0 paths: /admin: get: x-amazon-apigateway-auth: type: CUSTOM authorizer: AdminAuthorizer x-amazon-apigateway-integration: type: AWS_PROXY uri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:admin-backend/invocations httpMethod: POST passthroughBehavior: WHEN_NO_MATCH /admin/{proxy}: get: x-amazon-apigateway-integration: type: AWS_PROXY uri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:admin-backend/invocations httpMethod: POST passthroughBehavior: WHEN_NO_MATCH x-amazon-apigateway-authorizers: AdminAuthorizer: type: REQUEST authorizerUri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:admin-authorizer/invocations identitySource: method.request.header.Authorization authorizerResultTtlInSeconds: 300注意上面的架构/admin和/admin/{proxy}都集成到同一个admin-backend函数但只有/admin绑定了授权器。这就是复现“尾部斜杠绕过授权”的最小配置。导入 APIaws apigateway import-rest-api \ --region us-east-1 \ --body file://api-definition.yaml \ --parameters endpointConfigurationTypesREGIONAL导入后会得到一个 API ID假设就是abcdef1234。接下来需要部署 API并创建阶段例如prodaws apigateway create-deployment \ --rest-api-id abcdef1234 \ --stage-name prod如果你使用自定义域名还需要在 API Gateway 控制台里配置域名映射这里为了演示方便直接使用 API Gateway 默认生成的执行地址。需要注意OpenAPI 文件中的 Lambda ARN 和授权器 ARN 需要替换为你自己环境中的真实 ARN。如果 ARN 出错请求会报 500 错误。5.4 给授权器添加触发权限API Gateway 调用 Lambda Authorizer 需要权限否则会报类似 “The client is not authorized to invoke Lambda” 的错误。使用 AWS CLI 添加资源策略aws lambda add-permission \ --function-name admin-authorizer \ --statement-id apigateway-authorizer-invoke \ --action lambda:InvokeFunction \ --principal apigateway.amazonaws.com \ --source-arn arn:aws:execute-api:us-east-1:123456789012:abcdef1234/authorizers/*同样后端函数也需要授权aws lambda add-permission \ --function-name admin-backend \ --statement-id apigateway-backend-invoke \ --action lambda:InvokeFunction \ --principal apigateway.amazonaws.com \ --source-arn arn:aws:execute-api:us-east-1:123456789012:abcdef1234/prod/GET/admin/*这个source-arn中的*是为了让授权器可以匹配/admin和/admin/{proxy}两条路径。生产环境应按实际资源路径收紧。6. 运行结果与验证现在用 curl 来验证绕过效果。假设 API 的完整执行地址是https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod先请求正常的受保护路径/admin不携带 Authorization 头预期看到401 Unauthorizedcurl -i https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/admin因为 Lambda Authorizer 在identitySource中配置了读取Authorization请求头缺失或无法通过时 API Gateway 会拒绝请求。接着携带一个假令牌请求/admincurl -i \ -H Authorization: Bearer dummy-token \ https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/admin由于 Authorizer 对路径/admin返回 Allow这个请求会进入后端返回{message: admin data leaked, path: /admin, level: secret}接下来是关键的绕过测试。携带同样的假令牌请求/admin/curl -i \ -H Authorization: Bearer dummy-token \ https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/admin/如果复现成功你会看到请求被/admin/{proxy}资源捕获因为没有绑定授权器请求直接进入后端返回{message: admin data leaked, path: /admin/, level: secret}也就是说同样的令牌未经授权器校验就拿到了管理数据。如果/admin/返回的是403 Forbidden说明你的 Authorizer 在 /admin/{proxy} 上也被绑定了或者后端做了二次校验。恭喜你你的环境已经比“缺陷版”安全了一步。在 CloudWatch 日志中Lambda Authorizer 的输出会记录request path和method arn你可以通过日志确认请求/admin/时并没有打印授权相关的日志因为授权器根本没有被调用。7. 修复方案与推荐配置下面的修复方式从浅到深建议组合使用形成多层防御。7.1 方案一路径收敛确保所有通配符资源都配置授权器最直接的修复是只要存在通配符资源就要为所有方法配置与精确路径一致的授权器。比如/admin/{proxy}也应该绑定同一个AdminAuthorizer。在 OpenAPI 配置里给/admin/{proxy}加上同样授权配置/admin/{proxy}: get: x-amazon-apigateway-auth: type: CUSTOM authorizer: AdminAuthorizer x-amazon-apigateway-integration: type: AWS_PROXY uri: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:admin-backend/invocations httpMethod: POST passthroughBehavior: WHEN_NO_MATCH这样请求/admin/就会被路由到/admin/{proxy}但它在进入后端之前会先经过 Authorizer。此时如果 Authorizer 对/admin/返回 Deny请求就会被拦截。但这里还有一个坑如果 Authorizer 逻辑本身有问题比如对精确路径/admin返回 Allow对其他路径直接返回 Allow错误的 else 分支那么即使绑定了也没用。所以方案一必须搭配方案二。7.2 方案二修复 Lambda Authorizer 的策略逻辑无论 API Gateway 层怎么配置Lambda Authorizer 本身都应该遵循“默认拒绝”原则。也就是说只有显式匹配到允许规则时才返回 Allow其他任何情况一律返回 Deny。推荐写法如下# 文件路径lambda-authorizer/index.py import json def generate_policy(principal_id, effect, resource): return { principalId: principal_id, policyDocument: { Version: 2012-10-17, Statement: [ { Action: execute-api:Invoke, Effect: effect, Resource: resource } ] } } def normalize_path(path: str) - str: 去掉尾部斜杠统一路径表达。 if path ! / and path.endswith(/): return path.rstrip(/) return path def lambda_handler(event, context): method_arn event[methodArn] raw_path event.get(path, /) normalized normalize_path(raw_path) print(raw path:, raw_path) print(normalized path:, normalized) # 管理后台所有以 /admin 开头的路径都需要校验 # 注意这里必须使用前缀匹配不能只判断 if normalized.startswith(/admin): return generate_policy(admin-user, Allow, method_arn) # 非管理路径根据业务要求决定是否放行 return generate_policy(anonymous, Deny, method_arn)关键改进有三点路径归一化先把/admin/转成/admin消除尾部斜杠的差异前缀匹配使用startswith(/admin)而不是 /admin避免只保护了根路径默认拒绝没有明显匹配的路径都返回 Deny。需要提醒的是前缀匹配时要小心/administrator这类路径被误伤。更严谨的做法是使用正则^/admin(/|$)保证/admin后面必须是路径分隔符或字符串结束。import re if re.match(r^/admin(/|$), normalized): return generate_policy(admin-user, Allow, method_arn)7.3 方案三在 WAF 层拦截特殊路径形态如果 API Gateway 前有 AWS WAF可以添加一条规则直接拦截包含/admin/等异常尾部斜杠的请求。这样的好处是即使 API 配置里漏了授权器WAF 也能把恶意请求挡在门外。示例 WAF 规则{ Name: BlockAdminTrailingSlash, Priority: 1, Statement: { ByteMatchStatement: { SearchString: /admin/, FieldToMatch: { UriPath: {} }, TextTransformations: [ { Priority: 0, Type: NONE } ] } }, Action: { Block: {} }, VisibilityConfig: { SampledRequestsEnabled: true, CloudWatchMetricsEnabled: true, MetricName: BlockAdminTrailingSlash } }这条规则会拦截任何 URI 路径中包含/admin/字样的请求。由于 ByteMatchStatement 是子串匹配它也能拦住/admin/anything这类请求。如果你觉得过于严格可以调整为正则规则例如只拦截 admin 后面直接跟/且路径中带空段的请求。从实际场景看管理后台路径本身不应该被用户随意增加尾部斜杠所以这种拦截是合理的。7.4 方案四后端做最终兜底API Gateway 和 WAF 都属于边界防护如果配置出现历史遗留问题后端也需要有自己的防线。后端 Lambda 应该在函数入口统一校验实际请求路径# 文件路径lambda-backend/index.py import json import re def is_admin_request(path: str) - bool: # 归一化后判断是否属于管理路径 normalized path.rstrip(/) return bool(re.match(r^/admin(/|$), normalized)) def lambda_handler(event, context): path event.get(rawPath, event.get(path, /)) if is_admin_request(path): # 获取用户信息并做二次鉴权 claims event.get(requestContext, {}).get(authorizer, {}).get(claims, {}) user_role claims.get(custom:role, anonymous) if user_role ! admin: return { statusCode: 403, body: json.dumps({error: forbidden}) } return { statusCode: 200, headers: {Content-Type: application/json}, body: json.dumps({ message: admin data, path: path }) }这种做法的价值在于即使 API Gateway 被错误配置后端也能独立判断“当前请求是否来自管理员”。当然真正的生产环境不应该只依赖路径判断而应该基于身份凭证IAM 角色、JWT claims、API key 等做权限校验。7.5 推荐的安全组合从防御纵深的角度推荐组合是API Gateway 路径收敛确保通配符资源/{proxy}有与精确资源一致的授权配置Lambda Authorizer 默认拒绝所有未明确允许的请求返回 DenyWAF 规则拦截对/admin/这类异常尾部斜杠直接拦截后端二次校验对敏感业务路径在后端函数里基于用户角色或身份再次鉴权。四层都做才能在“某层配置失误”的场景下保住底线。7.6 修复后的验证命令完成上述修复后重新运行之前的验证命令# 应返回 401 或 403 curl -i https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/admin curl -i \ -H Authorization: Bearer dummy-token \ https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/admin/如果一切正常第一次请求会因为没有令牌被拒绝第二次请求会因为有令牌但 Authorizer 返回 Deny 而被拒绝。如果你已经把 Authorizer 逻辑改成了“非管理路径也返回 Deny”那么需要额外确认普通业务路径没有被误伤。比如curl -i \ -H Authorization: Bearer valid-token \ https://abcdef1234.execute-api.us-east-1.amazonaws.com/prod/users如果users路径也返回 401 或 403说明你的默认拒绝策略影响到了正常业务需要根据业务场景决定是否对users单独放行。8. 常见问题与排查思路问题现象可能原因排查方式解决方案请求/admin返回 500Lambda Authorizer 或后端函数报错查看 CloudWatch 日志检查函数 ARN 和权限确认 API Gateway 有lambda:InvokeFunction权限请求/admin返回 401未携带 Authorization 头或令牌无效检查请求头确认 Authorizer 的 identitySource 配置在请求头中添加Authorization: Bearer token请求/admin/返回 200通配符资源未配置授权器检查 OpenAPI 定义确认/admin/{proxy}是否绑定授权器为通配符资源绑定同一授权器携带错误 token 仍返回 200Authorizer 逻辑缺少默认拒绝查看 Authorizer 代码确认是否有 else 分支返回 Deny按“默认拒绝”原则修复代码修复后普通用户访问业务路径也失败默认拒绝策略覆盖了非管理路径检查 Authorizer 对非/admin路径的返回策略为业务路径单独配置 Allow 规则CloudWatch 中看不到 Authorizer 日志Authorizer 未被调用确认请求实际匹配到了哪个资源路径使用 API Gateway 控制台的 Test 功能验证路由第一直觉的排查步骤应该是先看 CloudWatch Logs确认“被拒绝的请求到底到达了哪一层”。如果 Authorizer 有日志说明请求确实先走了授权逻辑如果 Authorizer 没有日志但后端函数有日志说明请求匹配到了没有授权器的资源上——这就是问题所在。9. 最佳实践与生产环境建议9.1 路径设计要严格收敛在设计 API 时尽量避免“精确路径 通配路径”同时存在且权限配置不一致的情况。如果业务上确实需要/admin和/admin/{proxy}并存必须把两者放到同一个授权配置体系下。更简单的方案是统一使用/admin/{proxy}作为管理路径的入口所有/admin/...请求都走同一个授权器。9.2 授权器策略遵循“默认拒绝”这是整个文章最重要的建议。Lambda Authorizer 的返回值本质上是 IAM 策略而 API Gateway 只会根据返回值判断是否放行。如果你在每个分支里都显式返回 Allow 或 Deny并确保没有遗漏就不会出现“未匹配到规则就放行”的情况。最安全的写法是在函数末尾兜底返回 Deny。9.3 用自动化测试覆盖“路径变形”不要只在控制台里测试正常路径要建立自动化安全测试用例覆盖以下场景/admin不带令牌期望 401/admin带无效令牌期望 403/admin/带无效令牌期望 403/admin//带无效令牌期望 403/Admin、/ADMIN大小写变化是否会被授权路径中拼接..、%2f等编码字符时是否正确处理。这些测试可以集成到 CI/CD 流程中每次 API 配置变更后自动运行。9.4 启用 API Gateway 访问日志和 WAF 日志生产环境建议开启 API Gateway 的执行日志记录每次请求的路径、授权器结果和响应状态。同时启用 WAF 的 sampled requests一旦出现异常路径访问可以及时从日志中发现。如果发现大量/admin/、/admin//等测试请求说明可能有人在探测你的接口边界。9.5 权限最小化API Gateway 调用 Lambda Authorizer 和后端函数时都应该使用最小权限。特别要注意source-arn不能写成*而应该锁定到具体的 API ID、Stage 和方法路径。否则攻击者可能利用同一个 Lambda 函数被其他 API 调用的方式绕过审计。9.6 定期审计 API 配置建议团队定期导出 API Gateway 的 OpenAPI 定义用脚本扫描所有资源路径检查是否存在“授权器配置为空”的路径。如果你维护着多套环境这个脚本可以写成自动化巡检任务防止有人手动修改 API 配置时漏掉授权器。下面是一个简单的 Python 扫描脚本思路可以放在本地或 CI 中# 文件路径audit_gateway.py import json import sys def audit(openapi_file): with open(openapi_file, r, encodingutf-8) as f: spec json.load(f) issues [] for path, methods in spec.get(paths, {}).items(): for method, config in methods.items(): if method.lower() not in (get, post, put, delete, patch): continue auth config.get(x-amazon-apigateway-auth, {}) authorizer auth.get(authorizer) if not authorizer: issues.append(f{method.upper()} {path} - no authorizer) if issues: print(发现未授权保护的路径) for item in issues: print( -, item) sys.exit(1) else: print(所有路径均已配置授权器) if __name__ __main__: audit(api-definition.yaml)这个脚本只做最基础的检查生产环境可以补充更多规则比如检测{proxy}是否被重复授权、Authorization 头是否被正确识别等。10. 总结与后续实践方向尾部斜杠绕过 AWS API Gateway 授权本质上是“路由匹配”与“授权评估”之间的语义差。攻击者利用/admin/与/admin在 API Gateway 路径模型中的差异把请求导流到未配置授权器的通配符资源从而绕过保护。修复的关键不是单纯删掉斜杠而是保证所有路径形态都被同等的授权约束覆盖同时让授权器逻辑遵循默认拒绝原则。如果你正在做 API 安全管理建议先做一次自检打开 API Gateway 控制台逐个资源检查授权器配置把所有/{proxy}通配资源单独列出来确认它们的授权策略与精确路径一致然后把 Lambda Authorizer 代码里所有分支都看一遍确保不存在“未匹配即放行”的情况。这个问题的深层启示是安全不能只依赖“某一个配置正确”而要依赖“多个独立层级的校验同时生效”。API Gateway 的授权器只是第一道门后端函数、WAF、日志监控都应该具备各自的校验能力。否则下一次攻击者利用的也许不是尾部斜杠而是路径大小写、编码差异、HTTP 方法覆盖等同样隐蔽的边界问题。建议把文中的审计思路沉淀成团队的自动化检查清单让 API 配置的安全性从“人工保证”逐步变成“机制保证”。