# Redirect URI prefix and suffix matching

URL, Origin or redirect_uri compared by startsWith, endsWith or substring instead of exact parsed match

_Area: Web authentication and account takeover. Seen in 2 disclosed reports; latest 2026-08-17. Updated 2026-11-02._

## How it works
A trust decision compares a caller-supplied URL, host, or Origin against a registered value using prefix, suffix, or substring matching instead of exact equality at a real boundary. The assumption that "starts with our URL" or "ends with our hostname" means "is our URL" breaks because an attacker can control a string that shares the prefix or suffix but resolves elsewhere. The check passes, the code or message goes to the attacker's destination, and the user boundary collapses.

## Where it shows up
- `startsWith`/`endsWith`/`slice(-len)` comparisons on `redirect_uri`, `Origin`, or hostname [E2](https://bugbountylibrary.com/r/h1%3A387279.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md)
- A "partial match" flag passed into a redirect-URI resolver, with the later exact-match only logging [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md)
- Client-supplied `state` deserialized as raw JSON and trusted for `redirectUri` or PKCE values [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md)
- postMessage listeners delegating origin checks to a suffix helper [E2](https://bugbountylibrary.com/r/h1%3A387279.md)

## How to review
Find every place a URL, host, or Origin is compared to a trusted value. The invariant: the comparison must be exact on a parsed, normalized value, or anchored at a real boundary (a `.` before the hostname suffix, a `/` or end-of-string after the path prefix). For OAuth callbacks, confirm the token exchange re-validates that `redirect_uri` equals the one used at authorize time, and that `state` is server-issued and integrity-protected, not client JSON. For postMessage, confirm the check is `origin === trusted` or a dot-anchored hostname match.

## How finders spotted it
- Read the admin bundle's `message` listener, saw the origin check delegated to a helper doing hostname suffix comparison, and reasoned a lookalike domain would pass [E2](https://bugbountylibrary.com/r/h1%3A387279.md)
- Code review of the callback handler: noticed the third argument enabling partial matching and that the exact-match lookup only logs; noticed `state` is raw client JSON trusted server-side [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md)
- Read the OAuth docs, saw `redirect_uri` must be a subdirectory of the callback, tried `/../` and it worked; then tested whether get-token enforced redirect_uri binding and it didn't [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)

## Variants seen
- Hostname suffix match without dot boundary in postMessage origin check [E2](https://bugbountylibrary.com/r/h1%3A387279.md)
- Prefix match on redirect URI plus tamperable client-supplied state carrying PKCE [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md)
- Path traversal past subdirectory validation plus missing code-to-redirect binding [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)

## Impact and severity
Spoofed app messages and fake login overlays, plus privileged app-only messages [E2](https://bugbountylibrary.com/r/h1%3A387279.md); redirect turned into full token redemption and account takeover via injected PKCE [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md); leaked authorization codes redeemed for tokens exposing private repos [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md). Impact rises when the bypass chains to token redemption or code execution in a trusted origin, and when pre-approved clients silently grant escalated scopes [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md).

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md
- E2: https://bugbountylibrary.com/r/h1%3A387279.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-m44r-7c5h-m6mj.md
