Skip to content

getinfo: make sure CURLINFO_REDIRECT_URL does not contain creds - #23020

Closed
filtede98 wants to merge 3 commits into
curl:masterfrom
filtede98:fix-redirect-url-creds-leak
Closed

filtede98 wants to merge 3 commits into
curl:masterfrom
filtede98:fix-redirect-url-creds-leak

Conversation

@filtede98

Copy link
Copy Markdown
Contributor

Summary

Follow-up to commit 7a6bd02 (#23016): make sure data->info.wouldredirect (exposed via CURLINFO_REDIRECT_URL and %{redirect_url}) does not contain credentials inherited from the original request URL or netrc when resolving relative redirect targets in FOLLOW_FAKE mode.

Background & Mechanism

In 7a6bd02, CURLINFO_EFFECTIVE_URL was hardened to ensure credentials from netrc or userinfo are not leaked to callers.

However, when a redirect (HTTP 3xx) is received and CURLOPT_FOLLOWLOCATION is not enabled (or maxredirs is reached), Curl_http_follow() runs in type == FOLLOW_FAKE mode (lib/http.c:1289). In this branch:

  1. data->state.uh resolves the relative Location: target URL against the base URL, which retains the original user and password.
  2. curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0) generates the full redirect URL containing user:password@.
  3. follow_url was stored directly into data->info.wouldredirect (lib/http.c:1292) and returned verbatim to callers via curl_easy_getinfo(..., CURLINFO_REDIRECT_URL, ...) and %{redirect_url}.

Fix

In Curl_http_follow(), when type == FOLLOW_FAKE, strip credentials (CURLUPART_USER and CURLUPART_PASSWORD) from follow_url before storing it into data->info.wouldredirect.

Verification

Added tests/data/test1877 (mirroring test1876), verifying that %{redirect_url} on a 301 redirect with netrc credentials outputs the target URL without credentials. Validated with scripts/checksrc.pl.

Follow-up to 7a6bd02: make sure data->info.wouldredirect (exposed via CURLINFO_REDIRECT_URL and %{redirect_url}) does not contain credentials inherited from the original request URL or netrc when resolving relative redirect targets in FOLLOW_FAKE mode.

Verified in test 1877
@github-actions github-actions Bot added the tests label Sep 21, 2026
Comment thread tests/data/test1877
HTTP/1.1 301 redir
Content-Length: 0
Content-Type: text/html
Location: %TESTNUMBER0002

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This URL does not contain any credentials. Doesn't that make this test succeed even without this patch?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah right, it does the same as the effective url...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, since it's a relative redirect resolved against data->state.uh (which holds the netrc credentials), without this patch wouldredirect retains and outputs the user:password@ credentials, causing the test to fail. With the patch, they are stripped cleanly.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Sanitization failures can still expose the original credential-bearing redirect URL.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Prevents redirect metadata from exposing credentials inherited during relative URL resolution.

Changes:

  • Strips user and password fields from fake redirect URLs.
  • Adds and registers a netrc regression test.
File Description
tests/​data/​test1877 Verifies redirect URLs omit netrc credentials.
tests/​data/​Makefile.am Registers the regression test.
lib/​http.c Sanitizes stored redirect URLs.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/http.c Outdated
Comment on lines +1295 to +1307
if(u) {
if(!curl_url_set(u, CURLUPART_URL, follow_url,
CURLU_NON_SUPPORT_SCHEME |
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0)) &&
!curl_url_set(u, CURLUPART_USER, NULL, 0) &&
!curl_url_set(u, CURLUPART_PASSWORD, NULL, 0))
(void)curl_url_get(u, CURLUPART_URL, &nocred, CURLU_GET_EMPTY);
curl_url_cleanup(u);
}
if(nocred) {
curlx_free(follow_url);
follow_url = nocred;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to fail closed in 54d40c7: follow_url is now unconditionally freed and wouldredirect is set directly to nocred (NULL on allocation/parsing failure), preventing credential leakage under memory pressure.

Ensure that if curl_url() or URL redaction fails, the credential-bearing follow_url is freed and wouldredirect is set to NULL rather than leaking credentials under allocation failure.
When Location URL parsing fails (e.g. test 1518 with invalid space in URL), keep the raw duplicated Location header without credential stripping. When URL parsing succeeded, strip credentials and return CURLE_OUT_OF_MEMORY if curl_url() fails.
@bagder bagder closed this in ce35977 Sep 21, 2026
@bagder

bagder commented Sep 21, 2026

Copy link
Copy Markdown
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

3 participants