1
0
mirror of synced 2025-11-03 19:53:13 +03:00

Merge commit from fork

* Fix "Untrusted HTTP Header Handling (REMOTE*/LOCAL*)"

* Fix "Untrusted HTTP Header Handling (X-Forwarded-For)"

* Fix security problems in docker/main.cc
This commit is contained in:
yhirose
2025-10-27 19:54:12 -04:00
committed by GitHub
parent 11eed05ce7
commit ac9ebb0ee3
3 changed files with 355 additions and 43 deletions

View File

@@ -1132,6 +1132,8 @@ public:
Server &
set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
Server &set_trusted_proxies(const std::vector<std::string> &proxies);
Server &set_keep_alive_max_count(size_t count);
Server &set_keep_alive_timeout(time_t sec);
@@ -1170,6 +1172,9 @@ protected:
const std::function<void(Request &)> &setup_request);
std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
std::vector<std::string> trusted_proxies_;
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
@@ -4600,13 +4605,35 @@ inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
}
#endif
inline bool is_prohibited_header_name(const std::string &name) {
using udl::operator""_t;
switch (str2tag(name)) {
case "REMOTE_ADDR"_t:
case "REMOTE_PORT"_t:
case "LOCAL_ADDR"_t:
case "LOCAL_PORT"_t: return true;
default: return false;
}
}
inline bool has_header(const Headers &headers, const std::string &key) {
if (is_prohibited_header_name(key)) { return false; }
return headers.find(key) != headers.end();
}
inline const char *get_header_value(const Headers &headers,
const std::string &key, const char *def,
size_t id) {
if (is_prohibited_header_name(key)) {
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
std::string msg = "Prohibited header name '" + key + "' is specified.";
throw std::invalid_argument(msg);
#else
return "";
#endif
}
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
@@ -7501,6 +7528,12 @@ inline Server &Server::set_header_writer(
return *this;
}
inline Server &
Server::set_trusted_proxies(const std::vector<std::string> &proxies) {
trusted_proxies_ = proxies;
return *this;
}
inline Server &Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
return *this;
@@ -8289,6 +8322,40 @@ inline bool Server::dispatch_request_for_content_reader(
return false;
}
inline std::string
get_client_ip(const std::string &x_forwarded_for,
const std::vector<std::string> &trusted_proxies) {
// X-Forwarded-For is a comma-separated list per RFC 7239
std::vector<std::string> ip_list;
detail::split(x_forwarded_for.data(),
x_forwarded_for.data() + x_forwarded_for.size(), ',',
[&](const char *b, const char *e) {
auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));
ip_list.emplace_back(std::string(b + r.first, b + r.second));
});
for (size_t i = 0; i < ip_list.size(); ++i) {
auto ip = ip_list[i];
auto is_trusted_proxy =
std::any_of(trusted_proxies.begin(), trusted_proxies.end(),
[&](const std::string &proxy) { return ip == proxy; });
if (is_trusted_proxy) {
if (i == 0) {
// If the trusted proxy is the first IP, there's no preceding client IP
return ip;
} else {
// Return the IP immediately before the trusted proxy
return ip_list[i - 1];
}
}
}
// If no trusted proxy is found, return the first IP in the list
return ip_list.front();
}
inline bool
Server::process_request(Stream &strm, const std::string &remote_addr,
int remote_port, const std::string &local_addr,
@@ -8352,15 +8419,16 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
connection_closed = true;
}
req.remote_addr = remote_addr;
if (!trusted_proxies_.empty() && req.has_header("X-Forwarded-For")) {
auto x_forwarded_for = req.get_header_value("X-Forwarded-For");
req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);
} else {
req.remote_addr = remote_addr;
}
req.remote_port = remote_port;
req.set_header("REMOTE_ADDR", req.remote_addr);
req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
req.local_addr = local_addr;
req.local_port = local_port;
req.set_header("LOCAL_ADDR", req.local_addr);
req.set_header("LOCAL_PORT", std::to_string(req.local_port));
if (req.has_header("Accept")) {
const auto &accept_header = req.get_header_value("Accept");