http: trim custom header name before the Authorization drop

A custom header name padded with blanks (`Authorization :`) missed the
exact-length compare and slipped past the cross-host Authorization and
Cookie drop, forwarding the header to the redirect target. Trim the
parsed name in both the request and proxy CONNECT header builders.

Closes #22178
This commit is contained in:
Alhuda Khan 2026-06-26 02:18:07 +05:30 committed by Daniel Stenberg
parent 18475e662c
commit a36384ab94
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 104 additions and 1 deletions

View file

@ -1805,6 +1805,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
continue;
}
/* a field name is a token and carries no surrounding whitespace, so
trim the parsed name before matching. Otherwise `Authorization :`
(space before the colon) slips past the Authorization/Cookie check
below and gets forwarded to another host on a redirect. */
curlx_str_trimblanks(&name);
/* only send this if the contents was non-blank or done special */
if(data->state.aptr.host &&

View file

@ -129,6 +129,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
continue;
DEBUGASSERT(curlx_strlen(&name) && value);
/* trim surrounding whitespace so a padded field name (e.g.
`Authorization :`) cannot slip past the Authorization/Cookie check */
curlx_str_trimblanks(&name);
if(data->state.aptr.host &&
/* a Host: header was sent already, do not pass on any custom Host:
header as that will produce *two* in the same request! */

View file

@ -254,7 +254,7 @@ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \
test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \
test2088 test2089 test2090 test2091 test2092 \
test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \
test2108 test2109 \
test2108 test2109 test2113 \
\
test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \
test2208 \

94
tests/data/test2113 Normal file
View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
HTTP proxy
HTTP Basic auth
HTTP proxy Basic auth
followlocation
</keywords>
</info>
# Server-side
<reply>
<data>
HTTP/1.1 302 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Content-Type: text/html
Funny-head: yesyes
Location: http://goto.second.host.now/%TESTNUMBER0002
Content-Length: 8
Connection: close
contents
</data>
<data2>
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Content-Type: text/html
Funny-head: yesyes
Content-Length: 9
contents
</data2>
<datacheck>
HTTP/1.1 302 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Content-Type: text/html
Funny-head: yesyes
Location: http://goto.second.host.now/%TESTNUMBER0002
Content-Length: 8
Connection: close
HTTP/1.1 200 OK
Date: Tue, 09 Nov 2010 14:49:00 GMT
Server: test-server/fake swsclose
Content-Type: text/html
Funny-head: yesyes
Content-Length: 9
contents
</datacheck>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP custom Authorization: with whitespace before colon and redirect to new host
</name>
<command>
http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -H "Authorization : s3cr3t" --proxy-user testing:this --location
</command>
<features>
proxy
</features>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol crlf="yes">
GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1
Host: first.host.it.is
Proxy-Authorization: Basic %b64[testing:this]b64%
User-Agent: curl/%VERSION
Accept: */*
Proxy-Connection: Keep-Alive
Authorization : s3cr3t
GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1
Host: goto.second.host.now
Proxy-Authorization: Basic %b64[testing:this]b64%
User-Agent: curl/%VERSION
Accept: */*
Proxy-Connection: Keep-Alive
</protocol>
</verify>
</testcase>