我通过末尾斜杠绕过了 AWS API Gateway 的身份验证,获得了 1.2 万美元的赏金。
I bypassed AWS API Gateway auth with a trailing slash. Got $12K bounty

原始链接: https://theguptalog.blogspot.com/2026/04/i-bypassed-aws-api-gateway-auth-with.html

一位安全研究员在某金融科技公司的移动端 API 中发现了一个严重的身份验证绕过漏洞,该漏洞由 AWS HTTP API 中不一致的路径匹配机制引起。 尽管 `GET /v1/accounts` 会正确返回 401 未授权错误,但在路径末尾添加斜杠(即 `/v1/accounts/`)却会返回 200 OK 并显示完整的账户数据。此问题源于 AWS HTTP API 的“贪婪”路径匹配特性:授权程序会根据原始路径验证请求,但 API Gateway 随后会重写路径,去掉末尾的斜杠,并将请求转发给后端,而此时相关的授权上下文已丢失(`userId` 变为 `undefined`)。 由于后端集成未能独立验证 `userId`,该漏洞导致未经身份验证的用户可以访问敏感接口,甚至能够发起电汇转账。研究员通过成功执行一笔 0.01 美元的测试转账证实了该漏洞的存在。 该金融科技公司通过从 HTTP API 迁移到限制更严格的 REST API,并在后端 Lambda 函数中实施了冗余的 `userId` 验证来修复此问题。研究员因发现该漏洞获得了 12,000 美元的赏金。

最近的一则 Hacker News 讨论帖中,一位安全研究人员声称通过在 AWS API Gateway 的身份验证中添加一个简单的斜杠(trailing slash),成功绕过了鉴权并获得了 12,000 美元的漏洞奖励。 该漏洞凸显了一个常见的“HTTP 陷阱”:安全过滤器对路径斜杠处理不一致,导致攻击者能够绕过授权规则。评论者指出,此类问题往往源于基于正则表达式的匹配实现不当,而非 AWS 本身的缺陷。 讨论很快演变成多方面的争论: * **漏洞本质:** 许多用户表示,“通过添加字符绕过”是一个经典的、长期存在的安全问题。有人打趣说,这笔赏金奖励的是“知道在哪里删减斜杠”,而非修复工作的复杂性。 * **严重程度:** 虽然一些人认为 12,000 美元过多,但另一些人反驳称,如果该漏洞允许未经授权的转账,那么这笔支出是合理的,甚至相比现实世界中遭受攻击的损失来说还偏低。 * **可信度:** 怀疑论者对来源提出了质疑,指出其使用了 Blogspot 域名,并怀疑文章是由人工智能生成的。这引发了关于现代 Web 标准以及安全领域“AI 生成式编程”(vibe-coding)的额外讨论。
相关文章

原文

I was poking at a fintech’s mobile API and noticed something that made no sense. GET /v1/accounts returned 401. GET /v1/accounts/ returned 200 with full account data. One character. Completely different security posture.

What I was looking at

The API ran on AWS HTTP API — the newer, cheaper alternative to REST API. Lambda authorizer checked a JWT against Cognito, returned an IAM policy. Standard.

Routes in OpenAPI:

YAML

/v1/accounts:
  get:
    x-amazon-apigateway-integration:
      uri: arn:aws:apigateway:...

/v1/accounts/{accountId}:
  get:
    x-amazon-apigateway-integration:
      uri: arn:aws:apigateway:...

The authorizer ran on every request. But HTTP API makes two decisions: does this route exist, and does the authorizer allow it? Those two layers didn’t agree on what a “match” meant.

The weird results

I ran ffuf on the path. The results were… inconsistent.

RequestResponse
GET /v1/accounts401 Unauthorized
GET /v1/accounts/200 OK + full data
GET /v1/accounts//200 OK
GET /v1/accounts?foo=bar401 Unauthorized
GET /v1/accounts%2f404 Not Found

The pattern: any path that sort-of matched a route prefix triggered the authorizer, then fell through to the integration without re-checking auth.

HTTP API does greedy path matching by default. /v1/accounts/ matched /v1/accounts as a prefix. The authorizer ran and returned Allow. Then the integration executed — but the integration mapping was fuzzy. The path got rewritten, the auth context got dropped, and suddenly I was inside without a valid JWT.

How the bypass actually worked

I traced it carefully. The $default route in HTTP API is a catch-all. The fintech had set it to return 404. But they’d also attached a mock integration for health checks at some point. That mock didn’t check auth — just returned {"status": "ok"}.

But /v1/accounts/ wasn’t hitting the mock. It was hitting the real backend. API Gateway’s greedy match rewrote the trailing-slash path, stripped the slash, and forwarded to the /v1/accounts integration. The auth check happened on the original path. The integration ran on the rewritten path. The rewrite dropped the auth context.

I confirmed it with a custom header. The authorizer sets context.authorizer.userId. The integration reads it. When I hit /v1/accounts/, the integration received userId: undefined. The integration didn’t validate userId. It just returned all accounts for the API key — which wasn’t even required here because auth was supposed to be the JWT.

The real damage

Same bypass worked on POST /v1/transfers/. I could initiate wire transfers without a valid JWT.

The backend checked that fromAccount belonged to the user. But userId was undefined, so it defaulted to a system account. I stopped after one $0.01 test transfer. It went through.

Telling them

I wrote it up. Screenshots of the 401 vs 200. The ffuf output. The exact path rewrite behavior. They fixed it the next day.

Switched from HTTP API to REST API (stricter path matching)

Added userId validation in every Lambda, not just the authorizer.

I got $12,000 bounty for it. Planning to go to Dubai :)

联系我们 contact @ memedata.com