inspector: check Host header
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/108 PR-URL: https://github.com/nodejs-private/node-private/pull/102/ Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Родитель
22fc97d598
Коммит
bf48e2992d
|
@ -394,12 +394,7 @@ static int header_value_cb(http_parser* parser, const char* at, size_t length) {
|
|||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
state->parsing_value = true;
|
||||
if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 &&
|
||||
node::StringEqualNoCaseN(state->current_header.data(),
|
||||
SEC_WEBSOCKET_KEY_HEADER,
|
||||
sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) {
|
||||
state->ws_key.append(at, length);
|
||||
}
|
||||
state->headers[state->current_header].append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -472,10 +467,59 @@ static void handshake_failed(InspectorSocket* inspector) {
|
|||
// init_handshake references message_complete_cb
|
||||
static void init_handshake(InspectorSocket* inspector);
|
||||
|
||||
static std::string TrimPort(const std::string& host) {
|
||||
size_t last_colon_pos = host.rfind(":");
|
||||
if (last_colon_pos == std::string::npos)
|
||||
return host;
|
||||
size_t bracket = host.rfind("]");
|
||||
if (bracket == std::string::npos || last_colon_pos > bracket)
|
||||
return host.substr(0, last_colon_pos);
|
||||
return host;
|
||||
}
|
||||
|
||||
static bool IsIPAddress(const std::string& host) {
|
||||
if (host.length() >= 4 && host.front() == '[' && host.back() == ']')
|
||||
return true;
|
||||
int quads = 0;
|
||||
for (char c : host) {
|
||||
if (c == '.')
|
||||
quads++;
|
||||
else if (!isdigit(c))
|
||||
return false;
|
||||
}
|
||||
return quads == 3;
|
||||
}
|
||||
|
||||
static std::string HeaderValue(const struct http_parsing_state_s* state,
|
||||
const std::string& header) {
|
||||
bool header_found = false;
|
||||
std::string value;
|
||||
for (const auto& header_value : state->headers) {
|
||||
if (node::StringEqualNoCaseN(header_value.first.data(), header.data(),
|
||||
header.length())) {
|
||||
if (header_found)
|
||||
return "";
|
||||
value = header_value.second;
|
||||
header_found = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool IsAllowedHost(const std::string& host_with_port) {
|
||||
std::string host = TrimPort(host_with_port);
|
||||
return host.empty() || IsIPAddress(host)
|
||||
|| node::StringEqualNoCase(host.data(), "localhost")
|
||||
|| node::StringEqualNoCase(host.data(), "localhost6");
|
||||
}
|
||||
|
||||
static int message_complete_cb(http_parser* parser) {
|
||||
InspectorSocket* inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
struct http_parsing_state_s* state = inspector->http_parsing_state;
|
||||
if (parser->method != HTTP_GET) {
|
||||
state->ws_key = HeaderValue(state, "Sec-WebSocket-Key");
|
||||
|
||||
if (!IsAllowedHost(HeaderValue(state, "Host")) ||
|
||||
parser->method != HTTP_GET) {
|
||||
handshake_failed(inspector);
|
||||
} else if (!parser->upgrade) {
|
||||
if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "util-inl.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -38,6 +39,7 @@ struct http_parsing_state_s {
|
|||
std::string ws_key;
|
||||
std::string path;
|
||||
std::string current_header;
|
||||
std::map<std::string, std::string> headers;
|
||||
};
|
||||
|
||||
struct ws_state_s {
|
||||
|
|
|
@ -906,4 +906,35 @@ TEST_F(InspectorSocketTest, ErrorCleansUpTheSocket) {
|
|||
EXPECT_EQ(UV_EPROTO, err);
|
||||
}
|
||||
|
||||
static void HostCheckedForGet_handshake(enum inspector_handshake_event state,
|
||||
const std::string &path, bool *cont) {
|
||||
EXPECT_EQ(kInspectorHandshakeFailed, state);
|
||||
EXPECT_TRUE(path.empty());
|
||||
*cont = false;
|
||||
}
|
||||
|
||||
TEST_F(InspectorSocketTest, HostCheckedForGet) {
|
||||
handshake_delegate = HostCheckedForGet_handshake;
|
||||
const char WRITE_REQUEST[] = "GET /respond/withtext HTTP/1.1\r\n"
|
||||
"Host: notlocalhost:9222\r\n\r\n";
|
||||
send_in_chunks(WRITE_REQUEST, sizeof(WRITE_REQUEST) - 1);
|
||||
|
||||
expect_handshake_failure();
|
||||
assert_both_sockets_closed();
|
||||
}
|
||||
|
||||
TEST_F(InspectorSocketTest, HostCheckedForUpgrade) {
|
||||
handshake_delegate = HostCheckedForGet_handshake;
|
||||
const char UPGRADE_REQUEST[] = "GET /ws/path HTTP/1.1\r\n"
|
||||
"Host: nonlocalhost:9229\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Key: aaa==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n\r\n";
|
||||
send_in_chunks(UPGRADE_REQUEST, sizeof(UPGRADE_REQUEST) - 1);
|
||||
|
||||
expect_handshake_failure();
|
||||
assert_both_sockets_closed();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
Загрузка…
Ссылка в новой задаче