mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-27 12:31:28 +00:00
Strip Cookie headers on cross-origin redirect
Cookie and Cookie2 headers were forwarded to the new host when following a cross-origin redirect, even though Host, Proxy-Authorization, and Authorization were already stripped. Add them to the removal list so session cookies are not leaked to a different origin.
This commit is contained in:
1 parent
62d899feac
commit
0c1cc8c986
2 files changed
+86
-2
No files matched your search
@@ -13557,8 +13557,8 @@ inline bool ClientImpl::create_redirect_client(
|
|||||||
|
|
||||||
// Clean up request headers that are host/client specific
|
// Clean up request headers that are host/client specific
|
||||||
// Remove headers that should not be carried over to new host
|
// Remove headers that should not be carried over to new host
|
||||||
auto headers_to_remove =
|
auto headers_to_remove = std::vector<std::string>{
|
||||||
std::vector<std::string>{"Host", "Proxy-Authorization", "Authorization"};
|
"Host", "Proxy-Authorization", "Authorization", "Cookie", "Cookie2"};
|
||||||
|
|
||||||
for (const auto &header_name : headers_to_remove) {
|
for (const auto &header_name : headers_to_remove) {
|
||||||
auto it = req.headers.find(header_name);
|
auto it = req.headers.find(header_name);
|
||||||
|
|||||||
@@ -2773,6 +2773,90 @@ TEST(RedirectToDifferentPort, DoNotForwardCredentialsBearerToken) {
|
|||||||
[](Client &cli) { cli.set_bearer_token_auth("my-secret-token"); });
|
[](Client &cli) { cli.set_bearer_token_auth("my-secret-token"); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RedirectToDifferentPort, DoNotForwardCookie) {
|
||||||
|
Server svr1;
|
||||||
|
std::string captured_cookie;
|
||||||
|
bool target_hit = false;
|
||||||
|
svr1.Get("/target", [&](const Request &req, Response &res) {
|
||||||
|
captured_cookie = req.get_header_value("Cookie");
|
||||||
|
target_hit = true;
|
||||||
|
res.set_content("OK", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
int svr1_port = 0;
|
||||||
|
auto thread1 = std::thread([&]() {
|
||||||
|
svr1_port = svr1.bind_to_any_port(HOST);
|
||||||
|
svr1.listen_after_bind();
|
||||||
|
});
|
||||||
|
|
||||||
|
Server svr2;
|
||||||
|
svr2.Get("/redir", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_redirect(
|
||||||
|
"http://localhost:" + std::to_string(svr1_port) + "/target", 302);
|
||||||
|
});
|
||||||
|
|
||||||
|
int svr2_port = 0;
|
||||||
|
auto thread2 = std::thread([&]() {
|
||||||
|
svr2_port = svr2.bind_to_any_port(HOST);
|
||||||
|
svr2.listen_after_bind();
|
||||||
|
});
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
svr2.stop();
|
||||||
|
thread2.join();
|
||||||
|
svr1.stop();
|
||||||
|
thread1.join();
|
||||||
|
ASSERT_FALSE(svr2.is_running());
|
||||||
|
ASSERT_FALSE(svr1.is_running());
|
||||||
|
});
|
||||||
|
|
||||||
|
svr1.wait_until_ready();
|
||||||
|
svr2.wait_until_ready();
|
||||||
|
|
||||||
|
Client cli("localhost", svr2_port);
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
|
||||||
|
Headers headers = {{"Cookie", "session_id=SECRET; auth=PRIVATE"}};
|
||||||
|
auto res = cli.Get("/redir", headers);
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||||
|
EXPECT_TRUE(target_hit);
|
||||||
|
// Cookie MUST NOT be forwarded to a different host (GHSA-22mf-w2v3-r2jv)
|
||||||
|
EXPECT_TRUE(captured_cookie.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RedirectToSamePort, ForwardCookie) {
|
||||||
|
// A same-origin redirect (same scheme/host/port) should preserve the Cookie
|
||||||
|
// header so that ordinary session flows keep working.
|
||||||
|
Server svr;
|
||||||
|
std::string captured_cookie;
|
||||||
|
svr.Get("/redir", [&](const Request & /*req*/, Response &res) {
|
||||||
|
res.set_redirect("/target", 302);
|
||||||
|
});
|
||||||
|
svr.Get("/target", [&](const Request &req, Response &res) {
|
||||||
|
captured_cookie = req.get_header_value("Cookie");
|
||||||
|
res.set_content("OK", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
auto port = svr.bind_to_any_port(HOST);
|
||||||
|
auto thread = std::thread([&]() { svr.listen_after_bind(); });
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
svr.stop();
|
||||||
|
thread.join();
|
||||||
|
ASSERT_FALSE(svr.is_running());
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.wait_until_ready();
|
||||||
|
|
||||||
|
Client cli(HOST, port);
|
||||||
|
cli.set_follow_location(true);
|
||||||
|
|
||||||
|
Headers headers = {{"Cookie", "session_id=SECRET"}};
|
||||||
|
auto res = cli.Get("/redir", headers);
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||||
|
EXPECT_EQ("session_id=SECRET", captured_cookie);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(RedirectToDifferentPort, OverflowPortNumber) {
|
TEST(RedirectToDifferentPort, OverflowPortNumber) {
|
||||||
Server svr;
|
Server svr;
|
||||||
svr.Get("/redir", [&](const Request & /*req*/, Response &res) {
|
svr.Get("/redir", [&](const Request & /*req*/, Response &res) {
|
||||||
|
|||||||
Reference in new issue
Block a user