diff options
-rw-r--r-- | doc/api/cli.md | 11 | ||||
-rw-r--r-- | doc/node.1 | 6 | ||||
-rw-r--r-- | lib/_http_client.js | 4 | ||||
-rw-r--r-- | lib/_http_common.js | 13 | ||||
-rw-r--r-- | lib/_http_server.js | 4 | ||||
-rw-r--r-- | src/node_http_parser.cc | 7 | ||||
-rw-r--r-- | src/node_options.cc | 4 | ||||
-rw-r--r-- | src/node_options.h | 2 |
8 files changed, 47 insertions, 4 deletions
diff --git a/doc/api/cli.md b/doc/api/cli.md index d4b4da5249..171f6b8ad2 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -419,6 +419,16 @@ added: v9.0.0 Specify the `module` of a custom [experimental ECMAScript Module loader][]. `module` may be either a path to a file, or an ECMAScript Module name. +### `--insecure-http-parser` +<!-- YAML +added: REPLACEME +--> + +Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow +interoperability with non-conformant HTTP implementations. It may also allow +request smuggling and other HTTP attacks that rely on invalid headers being +accepted. Avoid using this option. + ### `--max-http-header-size=size` <!-- YAML added: v11.6.0 @@ -1064,6 +1074,7 @@ Node.js options that are allowed are: * `--http-parser` * `--icu-data-dir` * `--input-type` +* `--insecure-http-parser` * `--inspect-brk` * `--inspect-port`, `--debug-port` * `--inspect-publish-uid` diff --git a/doc/node.1 b/doc/node.1 index 5f98ba7091..5ea38be56e 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -213,6 +213,12 @@ Specify the .Ar module to use as a custom module loader. . +.It Fl -insecure-http-parser +Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow +interoperability with non-conformant HTTP implementations. It may also allow +request smuggling and other HTTP attacks that rely on invalid headers being +accepted. Avoid using this option. +. .It Fl -max-http-header-size Ns = Ns Ar size Specify the maximum size of HTTP headers in bytes. Defaults to 8KB. . diff --git a/lib/_http_client.js b/lib/_http_client.js index ece93d14e0..7888ce27d5 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -39,6 +39,7 @@ const { freeParser, parsers, HTTPParser, + isLenient, prepareError, } = require('_http_common'); const { OutgoingMessage } = require('_http_outgoing'); @@ -676,7 +677,8 @@ function tickOnSocket(req, socket) { req.socket = socket; parser.initialize(HTTPParser.RESPONSE, new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req), - req.maxHeaderSize || 0); + req.maxHeaderSize || 0, + isLenient()); parser.socket = socket; parser.outgoing = req; req.parser = parser; diff --git a/lib/_http_common.js b/lib/_http_common.js index dc6eba6333..f1386e1a09 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -28,6 +28,8 @@ const { const { setImmediate } = require('timers'); const { methods, HTTPParser } = internalBinding('http_parser'); +const { getOptionValue } = require('internal/options'); +const insecureHTTPParser = getOptionValue('--insecure-http-parser'); const FreeList = require('internal/freelist'); const incoming = require('_http_incoming'); @@ -237,6 +239,16 @@ function prepareError(err, parser, rawPacket) { err.message = `Parse Error: ${err.reason}`; } +let warnedLenient = false; + +function isLenient() { + if (insecureHTTPParser && !warnedLenient) { + warnedLenient = true; + process.emitWarning('Using insecure HTTP parsing'); + } + return insecureHTTPParser; +} + module.exports = { _checkInvalidHeaderChar: checkInvalidHeaderChar, _checkIsHttpToken: checkIsHttpToken, @@ -249,5 +261,6 @@ module.exports = { parsers, kIncomingMessage, HTTPParser, + isLenient, prepareError, }; diff --git a/lib/_http_server.js b/lib/_http_server.js index 192d0ddfb7..18b4e8d4a2 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -39,6 +39,7 @@ const { chunkExpression, kIncomingMessage, HTTPParser, + isLenient, _checkInvalidHeaderChar: checkInvalidHeaderChar, prepareError, } = require('_http_common'); @@ -410,7 +411,8 @@ function connectionListenerInternal(server, socket) { parser.initialize( HTTPParser.REQUEST, new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket), - server.maxHeaderSize || 0 + server.maxHeaderSize || 0, + isLenient(), ); parser.socket = socket; diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 0328dc7c0f..5e1da912e0 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -486,11 +486,13 @@ class Parser : public AsyncWrap, public StreamListener { static void Initialize(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); + bool lenient = args[3]->IsTrue(); uint64_t max_http_header_size = 0; CHECK(args[0]->IsInt32()); CHECK(args[1]->IsObject()); + if (args.Length() > 2) { CHECK(args[2]->IsNumber()); max_http_header_size = args[2].As<Number>()->Value(); @@ -515,7 +517,7 @@ class Parser : public AsyncWrap, public StreamListener { parser->set_provider_type(provider); parser->AsyncReset(args[1].As<Object>()); - parser->Init(type, max_http_header_size); + parser->Init(type, max_http_header_size, lenient); } template <bool should_pause> @@ -762,8 +764,9 @@ class Parser : public AsyncWrap, public StreamListener { } - void Init(llhttp_type_t type, uint64_t max_http_header_size) { + void Init(llhttp_type_t type, uint64_t max_http_header_size, bool lenient) { llhttp_init(&parser_, type, &settings); + llhttp_set_lenient(&parser_, lenient); header_nread_ = 0; url_.Reset(); status_message_.Reset(); diff --git a/src/node_options.cc b/src/node_options.cc index abf26fb781..831540f993 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -375,6 +375,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::heap_snapshot_signal, kAllowedInEnvironment); AddOption("--http-parser", "", NoOp{}, kAllowedInEnvironment); + AddOption("--insecure-http-parser", + "use an insecure HTTP parser that accepts invalid HTTP headers", + &EnvironmentOptions::insecure_http_parser, + kAllowedInEnvironment); AddOption("--input-type", "set module type for string input", &EnvironmentOptions::module_type, diff --git a/src/node_options.h b/src/node_options.h index c4cb5dc04f..7b3ae19fe6 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -158,6 +158,8 @@ class EnvironmentOptions : public Options { bool print_eval = false; bool force_repl = false; + bool insecure_http_parser = false; + bool tls_min_v1_0 = false; bool tls_min_v1_1 = false; bool tls_min_v1_2 = false; |