# JWT algorithm and claim confusion

Verifier accepts unpinned algorithms or skips issuer, tenant, or audience checks, so forged or cross-tenant tokens pass

_Area: Web authentication and account takeover. Seen in 9 disclosed reports; 2 confirmed bounties: $1,337 and $40,000; latest 2026-10-08. Updated 2026-10-23._

## How it works
A JWT verifier must answer two questions: was this token signed by the party I trust, and was it minted for me? This pattern is a verifier that answers only the first, or answers it against the wrong key. Three sub-patterns share the root: (1) unpinned algorithm, so a token can be signed with a key the verifier treats as a shared secret (algorithm confusion); (2) signature checked but claims binding the token to this app, tenant, or audience never compared; (3) a key-type guard that recognizes keys by text marker or byte prefix instead of parsing them, so an asymmetric public key falls through to the HMAC path. The broken assumption is "a valid signature means a valid token for us." A signature proves who signed, not who the token was for.

## Where it shows up
- Verifier accepts more than one algorithm, or derives the algorithm from the token header [E4](https://bugbountylibrary.com/r/h1%3A3800870.md)
- Managed-service auth (App Service, Functions) that validates signature only, with no app-side tenant/issuer check [E1](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md)
- OIDC clients that check the signature but never `aud` [E5](https://bugbountylibrary.com/r/h1%3A1889161.md)
- Social-login endpoints that trust a client-supplied provider token without checking its app/audience [E6](https://bugbountylibrary.com/r/h1%3A314808.md)
- HMAC key prep that sniffs key type by text marker or byte prefix instead of parsing the key [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-ww5h-9m49-7xx4.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-ffc3-869f-jxw9.md)[E7](https://bugbountylibrary.com/r/ghsa%3AGHSA-p4g4-x82p-q773.md)[E8](https://bugbountylibrary.com/r/ghsa%3AGHSA-r6x4-923q-g947.md)

## How to review
Invariant: every accepted token must be signed by a pinned algorithm using the trusted IdP's published keys, and its `iss`, `aud`, and tenant claims must match this application's configured values before any identity is trusted. Check that the algorithm is fixed in code, not read from the header; that the key set comes from the IdP's JWKS, not a shared secret; and that claim comparison runs before authorization. For multi-tenant apps, confirm an explicit tenant allow-list exists in app code, not just platform auth [E1](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md). For social login, confirm the provider token's app/audience is validated server-side [E6](https://bugbountylibrary.com/r/h1%3A314808.md). For HMAC key prep, confirm the guard parses the key (or rejects any non-secret input) rather than matching markers or prefixes, and that it agrees with the downstream loader on every accepted encoding [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-ffc3-869f-jxw9.md)[E7](https://bugbountylibrary.com/r/ghsa%3AGHSA-p4g4-x82p-q773.md)[E8](https://bugbountylibrary.com/r/ghsa%3AGHSA-r6x4-923q-g947.md).

## Variants seen
- HS256 token HMAC-signed with the RSA public key, accepted by an unpinned verifier [E4](https://bugbountylibrary.com/r/h1%3A3800870.md)
- Signature-only validation of a multi-tenant IdP token, letting any tenant's user in [E1](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md)
- Signature checked but `aud` never verified, so sibling-service tokens cross over [E5](https://bugbountylibrary.com/r/h1%3A1889161.md)
- Provider access token from an unrelated app accepted at a social-login endpoint [E6](https://bugbountylibrary.com/r/h1%3A314808.md)
- Key-type guard defeated by a non-whitespace prefix [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-ww5h-9m49-7xx4.md), a BOM [E8](https://bugbountylibrary.com/r/ghsa%3AGHSA-r6x4-923q-g947.md), loader-accepted PEM mutations (tabs, CR-only, single-line) [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-ffc3-869f-jxw9.md), or binary DER matching no text marker [E7](https://bugbountylibrary.com/r/ghsa%3AGHSA-p4g4-x82p-q773.md)

## Why fixes fail
E2's fix addressed the reported trigger (leading whitespace) rather than the class: `trim()` plus an anchored regex still lets any non-whitespace prefix fall through to HMAC. E8 repeats this with `lstrip()` and a BOM. E3 and E7 show the same root: a hand-rolled recognizer in front of a tolerant parser, so any encoding the parser accepts but the recognizer rejects reopens the hole.

## Impact and severity
Led to account takeover and CMS control enabling stored XSS that stole O365 tokens [E1](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md). Severity is high where the verifier guards write or admin paths; E4's report showed read-only access, so impact is gated by whether read and write endpoints share the same verifier. Missing tenant/audience checks widen reach across every tenant or sibling service sharing the IdP [E1](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md)[E5](https://bugbountylibrary.com/r/h1%3A1889161.md).

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-azure-active-directory-bing-misconfiguration.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-ww5h-9m49-7xx4.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-ffc3-869f-jxw9.md
- E4: https://bugbountylibrary.com/r/h1%3A3800870.md
- E5: https://bugbountylibrary.com/r/h1%3A1889161.md
- E6: https://bugbountylibrary.com/r/h1%3A314808.md
- E7: https://bugbountylibrary.com/r/ghsa%3AGHSA-p4g4-x82p-q773.md
- E8: https://bugbountylibrary.com/r/ghsa%3AGHSA-r6x4-923q-g947.md
