mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-11-01 13:20:38 +00:00
Implement request smuggling protection for duplicate Content-Length headers and add corresponding tests
This commit is contained in:
1 parent
d279eff4db
commit
1e97c28e36
2 files changed
+70
No files matched your search
@@ -6813,6 +6813,16 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
|||||||
header_count++;
|
header_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC 9110 Section 8.6: Reject requests with multiple Content-Length
|
||||||
|
// headers that have different values to prevent request smuggling.
|
||||||
|
auto cl_range = headers.equal_range("Content-Length");
|
||||||
|
if (cl_range.first != cl_range.second) {
|
||||||
|
const auto &first_val = cl_range.first->second;
|
||||||
|
for (auto it = std::next(cl_range.first); it != cl_range.second; ++it) {
|
||||||
|
if (it->second != first_val) { return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13051,6 +13051,66 @@ TEST(ClientInThreadTest, Issue2068) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RequestSmugglingTest, DuplicateContentLengthDifferentValues) {
|
||||||
|
auto handled = false;
|
||||||
|
|
||||||
|
Server svr;
|
||||||
|
svr.Post("/test", [&](const Request &, Response &) { handled = true; });
|
||||||
|
|
||||||
|
thread t = thread([&]() { svr.listen(HOST, PORT); });
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
svr.stop();
|
||||||
|
t.join();
|
||||||
|
ASSERT_FALSE(svr.is_running());
|
||||||
|
ASSERT_FALSE(handled);
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.wait_until_ready();
|
||||||
|
|
||||||
|
// Two Content-Length headers with different values — must be rejected
|
||||||
|
auto req = "POST /test HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 5\r\n"
|
||||||
|
"Content-Length: 10\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"hello";
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
ASSERT_TRUE(send_request(1, req, &response));
|
||||||
|
ASSERT_EQ("HTTP/1.1 400 Bad Request",
|
||||||
|
response.substr(0, response.find("\r\n")));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RequestSmugglingTest, DuplicateContentLengthSameValues) {
|
||||||
|
auto handled = false;
|
||||||
|
|
||||||
|
Server svr;
|
||||||
|
svr.Post("/test", [&](const Request &, Response &res) {
|
||||||
|
handled = true;
|
||||||
|
res.set_content("ok", "text/plain");
|
||||||
|
});
|
||||||
|
|
||||||
|
thread t = thread([&]() { svr.listen(HOST, PORT); });
|
||||||
|
auto se = detail::scope_exit([&] {
|
||||||
|
svr.stop();
|
||||||
|
t.join();
|
||||||
|
ASSERT_FALSE(svr.is_running());
|
||||||
|
ASSERT_TRUE(handled);
|
||||||
|
});
|
||||||
|
|
||||||
|
svr.wait_until_ready();
|
||||||
|
|
||||||
|
// Two Content-Length headers with same value — should be accepted (RFC 9110)
|
||||||
|
auto req = "POST /test HTTP/1.1\r\n"
|
||||||
|
"Content-Length: 5\r\n"
|
||||||
|
"Content-Length: 5\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"hello";
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
ASSERT_TRUE(send_request(1, req, &response));
|
||||||
|
ASSERT_EQ("HTTP/1.1 200 OK", response.substr(0, response.find("\r\n")));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(HeaderSmugglingTest, ChunkedTrailerHeadersMerged) {
|
TEST(HeaderSmugglingTest, ChunkedTrailerHeadersMerged) {
|
||||||
Server svr;
|
Server svr;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user