security: Reject cross-host redirects to prevent Authorization leak#4171
Open
mohammadmseet-hue wants to merge 1 commit intogoogle:masterfrom
Open
security: Reject cross-host redirects to prevent Authorization leak#4171mohammadmseet-hue wants to merge 1 commit intogoogle:masterfrom
mohammadmseet-hue wants to merge 1 commit intogoogle:masterfrom
Conversation
The client manually follows HTTP 301 redirects in two places by extracting the Location header (or the RedirectionError.Location field) and recursively issuing a new request through the client's auth transport. Because the new request reuses the same transport, any Authorization header configured via WithAuthToken is also sent to the redirect target. Go's net/http.Client strips Authorization headers on cross-domain redirects by default (see golang/go#35104), but the custom redirect handling in roundTripWithOptionalFollowRedirect and bareDoUntilFound bypasses that protection. A compromised GitHub Enterprise Server, a MitM on an HTTP GHE link, or any API response that can inject a Location header can therefore redirect the client to an attacker-controlled host and capture the user's Bearer token. This change validates that the redirect target's host matches the client's configured BaseURL.Host before following the redirect. Same-host redirects (the normal rate-limit-redirection case) are unaffected; cross-host targets are rejected with a descriptive error. Relative Location values continue to resolve against BaseURL as before. Tests cover both the cross-host rejection and the same-host follow path in roundTripWithOptionalFollowRedirect, plus cross-host rejection in bareDoUntilFound.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #4171 +/- ##
==========================================
- Coverage 93.70% 93.69% -0.02%
==========================================
Files 210 210
Lines 19692 19706 +14
==========================================
+ Hits 18453 18463 +10
- Misses 1045 1047 +2
- Partials 194 196 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
gmlewis
approved these changes
Apr 19, 2026
Collaborator
gmlewis
left a comment
There was a problem hiding this comment.
Thank you, @mohammadmseet-hue!
LGTM.
Awaiting second LGTM+Approval from any other contributor to this repo before merging.
cc: @stevehipwell - @alexandear - @zyfy29 - @Not-Dhananjay-Mishra - @munlicode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
roundTripWithOptionalFollowRedirectandbareDoUntilFoundmanually follow HTTP 301 redirects by reading theLocationheader and issuing a new request through the same*http.Client. Because the auth transport installed byWithAuthTokenis attached to that client, any redirect target receives the user'sAuthorization: Bearer <token>header.Go's
net/http.ClientstripsAuthorizationon cross-domain redirects by default (golang/go#35104). The custom redirect handling here bypasses that protection: a compromised GitHub Enterprise Server, an on-path attacker on an HTTP GHE link, or any attacker who can influence an API response'sLocationheader can point the client at an attacker-controlled host and capture the Bearer token.This PR validates that the redirect target's host matches
c.BaseURL.Hostbefore following the redirect. Relative locations still resolve againstBaseURLand are followed normally; cross-host targets are rejected with a descriptive error.Affected code paths
Both redirect helpers preserve the auth transport across the follow:
roundTripWithOptionalFollowRedirect—github/github.go(used byGetArchiveLink,GetBranch,DownloadArtifact,GetWorkflowJobLogs,GetWorkflowRunLogs,GetWorkflowRunAttemptLogs).bareDoUntilFound—github/github.go(used by the same endpoints whenRateLimitRedirectionalEndpoints=true).What the fix does
Client.checkRedirectHost(location string) error, which resolves aLocationvalue (absolute or relative) againstc.BaseURLand returns an error if the resulting host differs fromc.BaseURL.Host.roundTripWithOptionalFollowRedirectcallscheckRedirectHostbefore recursing into the new URL.bareDoUntilFoundperforms the same host comparison before callingreq.Clone(ctx)(sinceClonepreservesAuthorizationheaders).Same-host redirects — the normal case for GitHub's rate-limit redirection flow — continue to work unchanged.
Reproducer
A minimal reproducer with a fake "GitHub API" returning a 301 to an evil server captures the Bearer token on
v84.0.0:With this patch applied, the same PoC returns
refusing to follow cross-host redirect from "127.0.0.1:<api>" to "127.0.0.1:<evil>"and no request is issued to the evil server.Tests
TestBareDoUntilFound_RejectsCrossHostRedirect— 301 with cross-host Location is rejected.TestRoundTripWithOptionalFollowRedirect_RejectsCrossHostRedirect— same, for the other code path.TestRoundTripWithOptionalFollowRedirect_AllowsSameHostRedirect— same-host 301 is still followed (regression guard).TestBareDoUntilFound_redirectLoopandTestBareDoUntilFound_UnexpectedRedirectioncontinue to pass.Notes on scope
This PR only addresses the two 301-follow code paths. Two related patterns in which the library uses attacker-controlled URLs from API JSON bodies through the auth transport —
DownloadContents/DownloadContentsWithMeta(viadownload_url) andUploadReleaseAssetFromRelease(viaupload_url) — have the same underlying risk but are out of scope here to keep this change focused and easy to review. I'm happy to send a follow-up PR for those.