Skip to content
This repository was archived by the owner on Oct 16, 2021. It is now read-only.

Commit 942e61e

Browse files
committed
http: opt-in insecure HTTP header parsing
Backport 496736f Original commit message: Allow insecure HTTP header parsing. Make clear it is insecure. See: - nodejs/node#30553 - nodejs/node#27711 (comment) - nodejs/node#30515 PR-URL: nodejs/node#30567 Backport-PR-URL: nodejs/node#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 8672cc6 commit 942e61e

7 files changed

+61
-8
lines changed

doc/api/cli.md

+12
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,17 @@ Useful when activating the inspector by sending the `SIGUSR1` signal.
131131
Default host is 127.0.0.1.
132132

133133

134+
### `--insecure-http-parser`
135+
<!-- YAML
136+
added: REPLACEME
137+
-->
138+
139+
Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
140+
interoperability with non-conformant HTTP implementations. It may also allow
141+
request smuggling and other HTTP attacks that rely on invalid headers being
142+
accepted. Avoid using this option.
143+
144+
134145
### `--no-deprecation`
135146
<!-- YAML
136147
added: v0.8.0
@@ -476,6 +487,7 @@ Node.js options that are allowed are:
476487
- `--enable-fips`
477488
- `--force-fips`
478489
- `--icu-data-dir`
490+
- `--insecure-http-parser`
479491
- `--inspect-brk`
480492
- `--inspect-port`
481493
- `--inspect`

doc/node.1

+7
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ Activate inspector on host:port and break at start of user script.
110110
.BR \-\-inspect-port \fI=[host:]port\fR
111111
Set the host:port to be used when the inspector is activated.
112112

113+
.TP
114+
.BR \-\-insecure\-http\-parser
115+
Use an insecure HTTP parser that accepts invalid HTTP headers. This may allow
116+
interoperability with non-conformant HTTP implementations. It may also allow
117+
request smuggling and other HTTP attacks that rely on invalid headers being
118+
accepted. Avoid using this option.
119+
113120
.TP
114121
.BR \-\-max\-http\-header-size \fI=size\fR
115122
Specify the maximum size of HTTP headers in bytes. Defaults to 8KB.

lib/_http_client.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ const {
3131
debug,
3232
freeParser,
3333
httpSocketSetup,
34-
parsers
34+
parsers,
35+
isLenient
3536
} = require('_http_common');
3637
const { OutgoingMessage } = require('_http_outgoing');
3738
const Agent = require('_http_agent');
@@ -622,7 +623,9 @@ function tickOnSocket(req, socket) {
622623
var parser = parsers.alloc();
623624
req.socket = socket;
624625
req.connection = socket;
625-
parser.reinitialize(HTTPParser.RESPONSE, parser[is_reused_symbol]);
626+
parser.reinitialize(HTTPParser.RESPONSE,
627+
parser[is_reused_symbol],
628+
isLenient());
626629
if (process.domain) {
627630
process.domain.add(parser);
628631
}

lib/_http_common.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,16 @@ function checkInvalidHeaderChar(val) {
353353
return false;
354354
}
355355

356+
let warnedLenient = false;
357+
358+
function isLenient() {
359+
if (process.insecureHTTPParser && !warnedLenient) {
360+
warnedLenient = true;
361+
process.emitWarning('Using insecure HTTP parsing');
362+
}
363+
return process.insecureHTTPParser;
364+
}
365+
356366
module.exports = {
357367
_checkInvalidHeaderChar: checkInvalidHeaderChar,
358368
_checkIsHttpToken: checkIsHttpToken,
@@ -364,5 +374,6 @@ module.exports = {
364374
httpSocketSetup,
365375
methods,
366376
parsers,
367-
kIncomingMessage
377+
kIncomingMessage,
378+
isLenient
368379
};

lib/_http_server.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const {
3434
chunkExpression,
3535
httpSocketSetup,
3636
kIncomingMessage,
37-
_checkInvalidHeaderChar: checkInvalidHeaderChar
37+
_checkInvalidHeaderChar: checkInvalidHeaderChar,
38+
isLenient
3839
} = require('_http_common');
3940
const { OutgoingMessage } = require('_http_outgoing');
4041
const { outHeadersKey, ondrain, nowDate } = require('internal/http');
@@ -333,7 +334,11 @@ function connectionListenerInternal(server, socket) {
333334
socket.on('timeout', socketOnTimeout);
334335

335336
var parser = parsers.alloc();
336-
parser.reinitialize(HTTPParser.REQUEST, parser[is_reused_symbol]);
337+
parser.reinitialize(
338+
HTTPParser.REQUEST,
339+
parser[is_reused_symbol],
340+
isLenient()
341+
);
337342
parser.socket = socket;
338343

339344
// We are starting to wait for our headers.

src/node.cc

+13
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ std::string icu_data_dir; // NOLINT(runtime/string)
240240

241241
uint64_t max_http_header_size = 8 * 1024;
242242

243+
// Set in node.cc by ParseArgs when --insecure-http-parser is used.
244+
bool insecure_http_parser = false;
245+
243246
// used by C++ modules as well
244247
bool no_deprecation = false;
245248

@@ -3265,6 +3268,11 @@ void SetupProcessObject(Environment* env,
32653268
preload_modules.clear();
32663269
}
32673270

3271+
// --insecure-http-parser
3272+
if (insecure_http_parser) {
3273+
READONLY_PROPERTY(process, "insecureHTTPParser", True(env->isolate()));
3274+
}
3275+
32683276
// --no-deprecation
32693277
if (no_deprecation) {
32703278
READONLY_PROPERTY(process, "noDeprecation", True(env->isolate()));
@@ -3539,6 +3547,8 @@ static void PrintHelp() {
35393547
" --inspect-port=[host:]port\n"
35403548
" set host:port for inspector\n"
35413549
#endif
3550+
" --insecure-http-parser use an insecure HTTP parser that\n"
3551+
" accepts invalid HTTP headers\n"
35423552
" --no-deprecation silence deprecation warnings\n"
35433553
" --max-http-header-size Specify the maximum size of HTTP\n"
35443554
" headers in bytes. Defaults to 8KB.\n"
@@ -3686,6 +3696,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env,
36863696
static const char* whitelist[] = {
36873697
// Node options, sorted in `node --help` order for ease of comparison.
36883698
"--require", "-r",
3699+
"--insecure-http-parser",
36893700
"--inspect",
36903701
"--inspect-brk",
36913702
"--inspect-port",
@@ -3832,6 +3843,8 @@ static void ParseArgs(int* argc,
38323843
syntax_check_only = true;
38333844
} else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) {
38343845
force_repl = true;
3846+
} else if (strcmp(arg, "--insecure-http-parser") == 0) {
3847+
insecure_http_parser = true;
38353848
} else if (strcmp(arg, "--no-deprecation") == 0) {
38363849
no_deprecation = true;
38373850
} else if (strcmp(arg, "--napi-modules") == 0) {

src/node_http_parser.cc

+5-3
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class Parser : public AsyncWrap {
166166
current_buffer_len_(0),
167167
current_buffer_data_(nullptr) {
168168
Wrap(object(), this);
169-
Init(type);
169+
Init(type, false);
170170
}
171171

172172

@@ -476,6 +476,7 @@ class Parser : public AsyncWrap {
476476

477477
static void Reinitialize(const FunctionCallbackInfo<Value>& args) {
478478
Environment* env = Environment::GetCurrent(args);
479+
bool lenient = args[2]->IsTrue();
479480

480481
CHECK(args[0]->IsInt32());
481482
CHECK(args[1]->IsBoolean());
@@ -494,7 +495,7 @@ class Parser : public AsyncWrap {
494495
if (isReused) {
495496
parser->AsyncReset();
496497
}
497-
parser->Init(type);
498+
parser->Init(type, lenient);
498499
}
499500

500501

@@ -738,8 +739,9 @@ class Parser : public AsyncWrap {
738739
}
739740

740741

741-
void Init(enum http_parser_type type) {
742+
void Init(enum http_parser_type type, bool lenient) {
742743
http_parser_init(&parser_, type);
744+
parser_.lenient_http_headers = lenient;
743745
url_.Reset();
744746
status_message_.Reset();
745747
num_fields_ = 0;

0 commit comments

Comments
 (0)