摘要
当指定自定义主机名时,如果本次传输也启用了cookie引擎,则该主机名会被用于cookie匹配。即便原始提供的主机名在跨源重定向过程中已被移除,这一匹配行为依然存在。
cookiehost 是通过自定义的 Host 头设置的:
lib/http.c http_set_aptr_host
ptr = Curl_checkheaders(data, STRCONST("Host"));
if(ptr && (!data->state.this_is_a_follow || curl_strequal(data->state.first_host, conn->host.name))) {
/* 如果我们有给定的自定义 Host: 头,我们会提取其中的主机名,
以便稍后可能出于cookie的原因使用它。我们只允许在非重定向
的情况下使用自定义 Host: 头,因为在重定向的请求中设置 Host:
是非常冒险的。除非主机名与第一个请求的主机名相同! */
char *cookiehost;
CURLcode result = copy_custom_value(ptr, &cookiehost);
// ...
aptr->cookiehost = cookiehost;
}
cookiehost 既用于发送Cookie,也用于处理 Set-Cookie:
lib/http.c http_header_s
v = (data->cookies && data->state.cookie_engine) ? HD_VAL(hd, hdlen, "Set-Cookie:") : NULL;
// ...
/* 如果这里设置了自定义的 Host: 名称,则使用它,否则使用
* 真实的对端主机名。 */
const char *host = data->state.aptr.cookiehost ?
data->state.aptr.cookiehost : conn->host.name;
// ...
result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host,
data->state.up.path, secure_context);
lib/http.c http_cookies
const char *host = data->state.aptr.cookiehost ?
data->state.aptr.cookiehost : data->conn->host.name;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
由于 cookiehost 在重定向后未被清除,这一行为会在跨源重定向过程中持续存在。
libcurl 关于 CURLOPT_HTTPHEADER 的文档 [1] 中提到:
如果本次传输也启用了cookie引擎,指定的主机名将被用于cookie匹配...
但文档中并未明确警告这同样适用于跨源重定向。
受影响版本
已在 curl 8.18.0 上测试
复现步骤
user@pc:~$ curl -v -L -c cookies.txt -H "Host: example.com" --resolve b.com:8001:127.0.0.1 --resolve a.com:8000:127.0.0.1 a.com:8000
* Added b.com:8001:127.0.0.1 to DNS cache
* Added a.com:8000:127.0.0.1 to DNS cache
* Hostname a.com was found in DNS cache
* Trying 127.0.0.1:8000...
* Established connection to a.com (127.0.0.1 port 8000) from 127.0.0.1 port 56874
* using HTTP/1.x
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/8.18.0
> Accept: */*
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Found
< Server: BaseHTTP/0.6 Python/3.12.3
< Date: Mon, 19 Jan 2026 12:35:05 GMT
< Location: http://b.com:8001/
* Added cookie ccc="secret" for domain example.com, path /, expire 0
< Set-Cookie: ccc=secret; Path=/
< Content-Length: 0
<
* shutting down connection #0
* Clear auth, redirects to port from 8000 to 8001
* Hostname b.com was found in DNS cache
* Trying 127.0.0.1:8001...
* Established connection to b.com (127.0.0.1 port 8001) from 127.0.0.1 port 43966
* using HTTP/1.x
> GET / HTTP/1.0
> Host: b.com:8001
> User-Agent: curl/8.18.0
> Accept: */*
> Cookie: ccc=secret
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 302 Found
< Server: BaseHTTP/0.6 Python/3.12.3
< Date: Mon, 19 Jan 2026 12:35:05 GMT
* Added cookie bbb="test" for domain example.com, path /, expire 0
< Set-Cookie: bbb=test; Path=/
< Location: http://a.com:8000/check
< Content-Length: 0
<
* shutting down connection #1
* Clear auth, redirects to port from 8001 to 8000
* Hostname a.com was found in DNS cache
* Trying 127.0.0.1:8000...
* Established connection to a.com (127.0.0.1 port 8000) from 127.0.0.1 port 56882
* using HTTP/1.x
> GET /check HTTP/1.0
> Host: example.com
> User-Agent: curl/8.18.0
> Accept: */*
> Cookie: bbb=test; ccc=secret
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: BaseHTTP/0.6 Python/3.12.3
< Date: Mon, 19 Jan 2026 12:35:05 GMT
< Content-Length: 0
<
* shutting down connection #2
user@pc:~$ cat cookies.txt
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
example.com FALSE / FALSE 0 bbb test
example.com FALSE / FALSE 0 ccc secret
参考资料
影响
跨源Cookie泄露与注入风险。
官方回应
- bagder (curl 职员):这是预期行为。欢迎为文档提供更清晰的措辞建议,但我们的确尝试记录了这一点,任何实际尝试并测试此行为的应用程序都会发现它。这不是一个安全问题。
- 根据项目透明度的政策,此报告已请求公开披露。 biOK/hzhVF2yKaGc5mK8oeejIYuUYW8I3RsXQCFCiXX2FBas+esuFymsr4+a87Du