fix(oidc): parse loopback redirect_uri host instead of string-prefix (BUNYIP-106) #242

Merged
David merged 2 commits from fix/bunyip-106-loopback-redirect-uri-parse into main 2026-06-26 12:04:50 +02:00
Member

The authorize endpoint validated bare-loopback redirect_uris (RFC 8252) with starts_with("http://127.0.0.1:"), so http://127.0.0.1:1@evil.com/cb (whose parsed host is evil.com, with 127.0.0.1:1 in the userinfo) and http://127.0.0.1.evil.com/cb (host is a subdomain of evil.com) both passed the check. The endpoint then 302-redirected the victim's browser to the attacker host with the authorization code, an account-takeover-class leak that PKCE does not mitigate.

Replace the prefix test with redirect_uri_matches, which parses the request URI and, for bare-loopback registrations, requires scheme == http, an empty userinfo component, and a parsed host of 127.0.0.1 / ::1 / localhost, allowing only the port and path to vary. Non-loopback clients keep byte-for-byte exact matching. Add url to bunyip-oidc and a regression test module covering the userinfo-@ and subdomain-suffix bypasses, the genuine varying-port case, the non-http scheme, and the non-loopback exact-match path.

#BUNYIP-106

The authorize endpoint validated bare-loopback redirect_uris (RFC 8252) with `starts_with("http://127.0.0.1:")`, so `http://127.0.0.1:1@evil.com/cb` (whose parsed host is `evil.com`, with `127.0.0.1:1` in the userinfo) and `http://127.0.0.1.evil.com/cb` (host is a subdomain of `evil.com`) both passed the check. The endpoint then 302-redirected the victim's browser to the attacker host with the authorization code, an account-takeover-class leak that PKCE does not mitigate. Replace the prefix test with `redirect_uri_matches`, which parses the request URI and, for bare-loopback registrations, requires `scheme == http`, an empty userinfo component, and a parsed host of `127.0.0.1` / `::1` / `localhost`, allowing only the port and path to vary. Non-loopback clients keep byte-for-byte exact matching. Add `url` to bunyip-oidc and a regression test module covering the userinfo-`@` and subdomain-suffix bypasses, the genuine varying-port case, the non-http scheme, and the non-loopback exact-match path. #BUNYIP-106
fix(oidc): parse loopback redirect_uri host instead of string-prefix (BUNYIP-106)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 1m7s
Check / fmt + clippy + build + tests (pull_request) Successful in 40m58s
826726683b
The authorize endpoint validated bare-loopback redirect_uris (RFC 8252) with `starts_with("http://127.0.0.1:")`, so `http://127.0.0.1:1@evil.com/cb` (whose parsed host is `evil.com`, with `127.0.0.1:1` in the userinfo) and `http://127.0.0.1.evil.com/cb` (host is a subdomain of `evil.com`) both passed the check. The endpoint then 302-redirected the victim's browser to the attacker host with the authorization code, an account-takeover-class leak that PKCE does not mitigate.

Replace the prefix test with `redirect_uri_matches`, which parses the request URI and, for bare-loopback registrations, requires `scheme == http`, an empty userinfo component, and a parsed host of `127.0.0.1` / `::1` / `localhost`, allowing only the port and path to vary. Non-loopback clients keep byte-for-byte exact matching. Add `url` to bunyip-oidc and a regression test module covering the userinfo-`@` and subdomain-suffix bypasses, the genuine varying-port case, the non-http scheme, and the non-loopback exact-match path.

#BUNYIP-106
Merge branch 'main' into fix/bunyip-106-loopback-redirect-uri-parse
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 25s
Check / fmt + clippy + build + tests (pull_request) Successful in 19m13s
Create release / Create release from merged PR (pull_request) Has been skipped
2c06512c7a
David merged commit 6aff69a55b into main 2026-06-26 12:04:50 +02:00
David deleted branch fix/bunyip-106-loopback-redirect-uri-parse 2026-06-26 12:04:51 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!242
No description provided.