Skip to content

Commit 3a7a3be

Browse files
sam-githubtargos
authored andcommitted
http: opt-in insecure HTTP header parsing
Allow insecure HTTP header parsing. Make clear it is insecure. See: - #30553 - #27711 (comment) - #30515 PR-URL: #30567 Backport-PR-URL: #30473 Reviewed-By: Fedor Indutny <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Denys Otrishko <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 25f2df4 commit 3a7a3be

8 files changed

+46
-4
lines changed

doc/api/cli.md

+11
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,16 @@ added: v9.0.0
431431
Specify the `module` of a custom [experimental ECMAScript Module][] loader.
432432
`module` may be either a path to a file, or an ECMAScript Module name.
433433

434+
### `--insecure-http-parser`
435+
<!-- YAML
436+
added: REPLACEME
437+
-->
438+
439+
Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
440+
interoperability with non-conformant HTTP implementations. It may also allow
441+
request smuggling and other HTTP attacks that rely on invalid headers being
442+
accepted. Avoid using this option.
443+
434444
### `--max-http-header-size=size`
435445
<!-- YAML
436446
added: v11.6.0
@@ -1099,6 +1109,7 @@ Node.js options that are allowed are:
10991109
* `--http-server-default-timeout`
11001110
* `--icu-data-dir`
11011111
* `--input-type`
1112+
* `--insecure-http-parser`
11021113
* `--inspect-brk`
11031114
* `--inspect-port`, `--debug-port`
11041115
* `--inspect-publish-uid`

doc/node.1

+6
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,12 @@ Specify the
232232
as a custom loader, to load
233233
.Fl -experimental-modules .
234234
.
235+
.It Fl -insecure-http-parser
236+
Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
237+
interoperability with non-conformant HTTP implementations. It may also allow
238+
request smuggling and other HTTP attacks that rely on invalid headers being
239+
accepted. Avoid using this option.
240+
.
235241
.It Fl -max-http-header-size Ns = Ns Ar size
236242
Specify the maximum size of HTTP headers in bytes. Defaults to 8KB.
237243
.

lib/_http_client.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const {
3232
freeParser,
3333
parsers,
3434
HTTPParser,
35+
isLenient,
3536
prepareError,
3637
} = require('_http_common');
3738
const { OutgoingMessage } = require('_http_outgoing');
@@ -655,7 +656,8 @@ function tickOnSocket(req, socket) {
655656
req.socket = socket;
656657
req.connection = socket;
657658
parser.initialize(HTTPParser.RESPONSE,
658-
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req));
659+
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
660+
isLenient());
659661
parser.socket = socket;
660662
parser.outgoing = req;
661663
req.parser = parser;

lib/_http_common.js

+12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const { getOptionValue } = require('internal/options');
2929
const { methods, HTTPParser } =
3030
getOptionValue('--http-parser') === 'legacy' ?
3131
internalBinding('http_parser') : internalBinding('http_parser_llhttp');
32+
const insecureHTTPParser = getOptionValue('--insecure-http-parser');
3233

3334
const FreeList = require('internal/freelist');
3435
const incoming = require('_http_incoming');
@@ -238,6 +239,16 @@ function prepareError(err, parser, rawPacket) {
238239
err.message = `Parse Error: ${err.reason}`;
239240
}
240241

242+
let warnedLenient = false;
243+
244+
function isLenient() {
245+
if (insecureHTTPParser && !warnedLenient) {
246+
warnedLenient = true;
247+
process.emitWarning('Using insecure HTTP parsing');
248+
}
249+
return insecureHTTPParser;
250+
}
251+
241252
module.exports = {
242253
_checkInvalidHeaderChar: checkInvalidHeaderChar,
243254
_checkIsHttpToken: checkIsHttpToken,
@@ -250,5 +261,6 @@ module.exports = {
250261
parsers,
251262
kIncomingMessage,
252263
HTTPParser,
264+
isLenient,
253265
prepareError,
254266
};

lib/_http_server.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const {
3939
chunkExpression,
4040
kIncomingMessage,
4141
HTTPParser,
42+
isLenient,
4243
_checkInvalidHeaderChar: checkInvalidHeaderChar,
4344
prepareError,
4445
} = require('_http_common');
@@ -385,7 +386,8 @@ function connectionListenerInternal(server, socket) {
385386
// https://github.com/nodejs/node/pull/21313
386387
parser.initialize(
387388
HTTPParser.REQUEST,
388-
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket)
389+
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
390+
isLenient(),
389391
);
390392
parser.socket = socket;
391393

src/node_http_parser_impl.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ class Parser : public AsyncWrap, public StreamListener {
501501

502502
static void Initialize(const FunctionCallbackInfo<Value>& args) {
503503
Environment* env = Environment::GetCurrent(args);
504+
bool lenient = args[2]->IsTrue();
504505

505506
CHECK(args[0]->IsInt32());
506507
CHECK(args[1]->IsObject());
@@ -521,7 +522,7 @@ class Parser : public AsyncWrap, public StreamListener {
521522

522523
parser->set_provider_type(provider);
523524
parser->AsyncReset(args[1].As<Object>());
524-
parser->Init(type);
525+
parser->Init(type, lenient);
525526
}
526527

527528
template <bool should_pause>
@@ -805,12 +806,14 @@ class Parser : public AsyncWrap, public StreamListener {
805806
}
806807

807808

808-
void Init(parser_type_t type) {
809+
void Init(parser_type_t type, bool lenient) {
809810
#ifdef NODE_EXPERIMENTAL_HTTP
810811
llhttp_init(&parser_, type, &settings);
812+
llhttp_set_lenient(&parser_, lenient);
811813
header_nread_ = 0;
812814
#else /* !NODE_EXPERIMENTAL_HTTP */
813815
http_parser_init(&parser_, type);
816+
parser_.lenient_http_headers = lenient;
814817
#endif /* NODE_EXPERIMENTAL_HTTP */
815818
url_.Reset();
816819
status_message_.Reset();

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
416416
"(default: 120000)",
417417
&EnvironmentOptions::http_server_default_timeout,
418418
kAllowedInEnvironment);
419+
AddOption("--insecure-http-parser",
420+
"Use an insecure HTTP parser that accepts invalid HTTP headers",
421+
&EnvironmentOptions::insecure_http_parser,
422+
kAllowedInEnvironment);
419423
AddOption("--input-type",
420424
"set module type for string input",
421425
&EnvironmentOptions::module_type,

src/node_options.h

+2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class EnvironmentOptions : public Options {
159159
bool print_eval = false;
160160
bool force_repl = false;
161161

162+
bool insecure_http_parser = false;
163+
162164
bool tls_min_v1_0 = false;
163165
bool tls_min_v1_1 = false;
164166
bool tls_min_v1_2 = false;

0 commit comments

Comments
 (0)