为什么 CookieContainer 不替换同名 cookie?
Why CookieContainer does not replace cookies with the same name?
问题
我正试图从一个著名的网站上抓取数据,并发现了一个问题。
此站点使用 cookie 进行身份验证,它设置 cookie,然后在身份验证流程中替换它。
我的问题是 CookieContainer
不会替换具有相同名称、相同域的 cookie,但在第二种情况下,域以点开头。
但是任何浏览器或 Postman 都会这样做。
因此结果是 double-sent cookie,并且该站点无法通过身份验证流程。
示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NUnit.Framework;
...
[Test]
public void Cookies()
{
CookieContainer container = new CookieContainer();
Uri uri = new Uri("https://example.com/item");
string cookie1 = "myCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";
string cookie2 = "myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";
container.SetCookies(uri, cookie1);
container.SetCookies(uri, cookie2);
List<Cookie> cookies = container.GetCookies(uri).ToList();
foreach (Cookie cookie in cookies)
{
Console.WriteLine(cookie);
}
Assert.AreEqual(1, cookies.Count);
}
输出为:
myCookie=value1
myCookie=value2
但预期输出是:
myCookie=value2
为什么 SetCookies
?
SetCookies
方法在 System.Net.Http
的 CookieHelper
which is used inside of Http2Stream
的默认实现中。
当HttpClientHandler
的UseCookies
属性设置为true时使用:
HttpClientHandler handler = new HttpClientHandler()
{
CookieContainer = new CookieContainer(),
UseCookies = true,
};
var client = new HttpClient(handler);
//Make requests
我的运行时版本是 NET 5
,因此 HttpClientHandler
隐藏 SocketsHttpHandler
实现(如果有必要)。
我的问题:
- 为什么会发生这种情况?我无法在我可用的任何浏览器中复制此行为
- 除了在
Set-Cookie
headers 中手动处理 cookie 之外,是否有可能以某种方式修复此行为?
提前致谢!
UPD 1:样本 mockserver
第 1 步:在主机文件中将 example.com 设置为本地主机。
127.0.0.1 example.com
第 2 步:创建模拟服务器
initializerJson.json
[
{
"httpRequest": {
"method": "GET",
"path": "/1",
"secure": true
},
"httpResponse": {
"statusCode": 200,
"headers": {
"Set-Cookie": [
"myCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
]
},
"body": "1"
}
},
{
"httpRequest": {
"method": "GET",
"path": "/2",
"secure": true
},
"httpResponse": {
"statusCode": 200,
"headers": {
"Set-Cookie": [
"myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
]
},
"body": "2"
}
},
{
"httpRequest": {
"method": "GET",
"path": "/3",
"secure": true
},
"httpResponseTemplate": {
"template": "cookie = request.headers.Cookie; return { statusCode: 200, body: cookie }; ",
"templateType": "JAVASCRIPT"
}
}
]
docker-compose.yml
version: "2.4"
services:
mockServer:
image: mockserver/mockserver:latest
ports:
- 5000:1080
environment:
MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties
MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json
volumes:
- type: bind
source: .
target: /config
运行 终端中的模拟服务器:
docker-compose up
之后,您将在端口 5000 上拥有一个具有 3 个端点的 http 服务器:
/1
- 设置 cookie myCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly
/2
- 设置 cookie myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly
/3
- Returns cookie 发送到服务器
第 3 步:测试它
测试流程为:
- 请求
https://example.com:5000/1
端点
- 请求
https://example.com:5000/2
端点
- 请求
https://example.com:5000/3
端点并检查响应
示例 C# 代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NUnit.Framework;
...
[Test]
public async Task CheckCookies()
{
CookieContainer container = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler()
{
CookieContainer = container,
UseCookies = true,
//Bypass self-signed https cert
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
};
HttpClient client = new HttpClient(handler);
await client.GetAsync("https://example.com:5000/1");
await client.GetAsync("https://example.com:5000/2");
var response = await client.GetAsync("https://example.com:5000/3");
string responseText = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseText);
Assert.AreEqual("[ \"myCookie=value2\" ]", responseText);
}
结果
经过测试得到如下结果:
- Chrome 97:
[ "myCookie=value2" ]
- 火狐 97.0b6:
[ "myCookie=value2" ]
- 邮递员 8.11.1
[ "myCookie=value2" ]
- 上面的代码
[ "myCookie=value1; myCookie=value2" ]
有什么建议吗?
这是 dotnet 运行时中的错误:https://github.com/dotnet/runtime/issues/60628
问题
我正试图从一个著名的网站上抓取数据,并发现了一个问题。
此站点使用 cookie 进行身份验证,它设置 cookie,然后在身份验证流程中替换它。
我的问题是 CookieContainer
不会替换具有相同名称、相同域的 cookie,但在第二种情况下,域以点开头。
但是任何浏览器或 Postman 都会这样做。
因此结果是 double-sent cookie,并且该站点无法通过身份验证流程。
示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NUnit.Framework;
...
[Test]
public void Cookies()
{
CookieContainer container = new CookieContainer();
Uri uri = new Uri("https://example.com/item");
string cookie1 = "myCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";
string cookie2 = "myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";
container.SetCookies(uri, cookie1);
container.SetCookies(uri, cookie2);
List<Cookie> cookies = container.GetCookies(uri).ToList();
foreach (Cookie cookie in cookies)
{
Console.WriteLine(cookie);
}
Assert.AreEqual(1, cookies.Count);
}
输出为:
myCookie=value1
myCookie=value2
但预期输出是:
myCookie=value2
为什么 SetCookies
?
SetCookies
方法在 System.Net.Http
的 CookieHelper
which is used inside of Http2Stream
的默认实现中。
当HttpClientHandler
的UseCookies
属性设置为true时使用:
HttpClientHandler handler = new HttpClientHandler()
{
CookieContainer = new CookieContainer(),
UseCookies = true,
};
var client = new HttpClient(handler);
//Make requests
我的运行时版本是 NET 5
,因此 HttpClientHandler
隐藏 SocketsHttpHandler
实现(如果有必要)。
我的问题:
- 为什么会发生这种情况?我无法在我可用的任何浏览器中复制此行为
- 除了在
Set-Cookie
headers 中手动处理 cookie 之外,是否有可能以某种方式修复此行为?
提前致谢!
UPD 1:样本 mockserver
第 1 步:在主机文件中将 example.com 设置为本地主机。
127.0.0.1 example.com
第 2 步:创建模拟服务器
initializerJson.json
[
{
"httpRequest": {
"method": "GET",
"path": "/1",
"secure": true
},
"httpResponse": {
"statusCode": 200,
"headers": {
"Set-Cookie": [
"myCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
]
},
"body": "1"
}
},
{
"httpRequest": {
"method": "GET",
"path": "/2",
"secure": true
},
"httpResponse": {
"statusCode": 200,
"headers": {
"Set-Cookie": [
"myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
]
},
"body": "2"
}
},
{
"httpRequest": {
"method": "GET",
"path": "/3",
"secure": true
},
"httpResponseTemplate": {
"template": "cookie = request.headers.Cookie; return { statusCode: 200, body: cookie }; ",
"templateType": "JAVASCRIPT"
}
}
]
docker-compose.yml
version: "2.4"
services:
mockServer:
image: mockserver/mockserver:latest
ports:
- 5000:1080
environment:
MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties
MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json
volumes:
- type: bind
source: .
target: /config
运行 终端中的模拟服务器:
docker-compose up
之后,您将在端口 5000 上拥有一个具有 3 个端点的 http 服务器:
/1
- 设置 cookiemyCookie=value1; Domain=example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly
/2
- 设置 cookiemyCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly
/3
- Returns cookie 发送到服务器
第 3 步:测试它
测试流程为:
- 请求
https://example.com:5000/1
端点 - 请求
https://example.com:5000/2
端点 - 请求
https://example.com:5000/3
端点并检查响应
示例 C# 代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NUnit.Framework;
...
[Test]
public async Task CheckCookies()
{
CookieContainer container = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler()
{
CookieContainer = container,
UseCookies = true,
//Bypass self-signed https cert
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
};
HttpClient client = new HttpClient(handler);
await client.GetAsync("https://example.com:5000/1");
await client.GetAsync("https://example.com:5000/2");
var response = await client.GetAsync("https://example.com:5000/3");
string responseText = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseText);
Assert.AreEqual("[ \"myCookie=value2\" ]", responseText);
}
结果
经过测试得到如下结果:
- Chrome 97:
[ "myCookie=value2" ]
- 火狐 97.0b6:
[ "myCookie=value2" ]
- 邮递员 8.11.1
[ "myCookie=value2" ]
- 上面的代码
[ "myCookie=value1; myCookie=value2" ]
有什么建议吗?
这是 dotnet 运行时中的错误:https://github.com/dotnet/runtime/issues/60628