-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
deps,http: http_parser set max header size to 8KB
CVE-2018-12121 PR-URL: nodejs-private/node-private#143 Ref: nodejs-private/security#139 Ref: nodejs-private/http-parser-private#2 Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Anna Henningsen <anna@addaleax.net>
- Loading branch information
Showing
4 changed files
with
162 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
'use strict'; | ||
|
||
const assert = require('assert'); | ||
const common = require('../common'); | ||
const http = require('http'); | ||
const net = require('net'); | ||
const MAX = 8 * 1024; // 8KB | ||
|
||
// Verify that we cannot receive more than 8KB of headers. | ||
|
||
function once(cb) { | ||
let called = false; | ||
return () => { | ||
if (!called) { | ||
called = true; | ||
cb(); | ||
} | ||
}; | ||
} | ||
|
||
function finished(client, callback) { | ||
'abort error end'.split(' ').forEach((e) => { | ||
client.on(e, once(() => setImmediate(callback))); | ||
}); | ||
} | ||
|
||
function fillHeaders(headers, currentSize, valid = false) { | ||
// Generate valid headers | ||
if (valid) { | ||
// TODO(mcollina): understand why -9 is needed instead of -1 | ||
headers = headers.slice(0, -9); | ||
} | ||
return headers + '\r\n\r\n'; | ||
} | ||
|
||
const timeout = common.platformTimeout(10); | ||
|
||
function writeHeaders(socket, headers) { | ||
const array = []; | ||
|
||
// this is off from 1024 so that \r\n does not get split | ||
const chunkSize = 1000; | ||
let last = 0; | ||
|
||
for (let i = 0; i < headers.length / chunkSize; i++) { | ||
const current = (i + 1) * chunkSize; | ||
array.push(headers.slice(last, current)); | ||
last = current; | ||
} | ||
|
||
// safety check we are chunking correctly | ||
assert.strictEqual(array.join(''), headers); | ||
|
||
next(); | ||
|
||
function next() { | ||
if (socket.write(array.shift())) { | ||
if (array.length === 0) { | ||
socket.end(); | ||
} else { | ||
setTimeout(next, timeout); | ||
} | ||
} else { | ||
socket.once('drain', next); | ||
} | ||
} | ||
} | ||
|
||
function test1() { | ||
let headers = | ||
'HTTP/1.1 200 OK\r\n' + | ||
'Content-Length: 0\r\n' + | ||
'X-CRASH: '; | ||
|
||
// OK, Content-Length, 0, X-CRASH, aaa... | ||
const currentSize = 2 + 14 + 1 + 7; | ||
headers = fillHeaders(headers, currentSize); | ||
|
||
const server = net.createServer((sock) => { | ||
sock.once('data', (chunk) => { | ||
writeHeaders(sock, headers); | ||
sock.resume(); | ||
}); | ||
}); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const port = server.address().port; | ||
const client = http.get({ port: port }, common.mustNotCall(() => {})); | ||
|
||
client.on('error', common.mustCall((err) => { | ||
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | ||
server.close(); | ||
setImmediate(test2); | ||
})); | ||
})); | ||
} | ||
|
||
const test2 = common.mustCall(() => { | ||
let headers = | ||
'GET / HTTP/1.1\r\n' + | ||
'Host: localhost\r\n' + | ||
'Agent: node\r\n' + | ||
'X-CRASH: '; | ||
|
||
// /, Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | ||
headers = fillHeaders(headers, currentSize); | ||
|
||
const server = http.createServer(common.mustNotCall()); | ||
|
||
server.on('clientError', common.mustCall((err) => { | ||
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | ||
})); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const client = net.connect(server.address().port); | ||
client.on('connect', () => { | ||
writeHeaders(client, headers); | ||
client.resume(); | ||
}); | ||
|
||
finished(client, common.mustCall((err) => { | ||
server.close(); | ||
setImmediate(test3); | ||
})); | ||
})); | ||
}); | ||
|
||
const test3 = common.mustCall(() => { | ||
let headers = | ||
'GET / HTTP/1.1\r\n' + | ||
'Host: localhost\r\n' + | ||
'Agent: node\r\n' + | ||
'X-CRASH: '; | ||
|
||
// /, Host, localhost, Agent, node, X-CRASH, a... | ||
const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | ||
headers = fillHeaders(headers, currentSize, true); | ||
|
||
const server = http.createServer(common.mustCall((req, res) => { | ||
res.end('hello world'); | ||
setImmediate(server.close.bind(server)); | ||
})); | ||
|
||
server.listen(0, common.mustCall(() => { | ||
const client = net.connect(server.address().port); | ||
client.on('connect', () => { | ||
writeHeaders(client, headers); | ||
client.resume(); | ||
}); | ||
})); | ||
}); | ||
|
||
test1(); |
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record, this commit on master (same on v11.x in 74e01d0) is incomplete thanks to a lot of rebasing and juggling that had to go on in the week before release thanks to some unexpected bugs found once we had full CI access.
Line 28 in test/sequential/test-http-max-http-headers.js should have the following:
But it was pulled up in to a later commit, af8d9e3, where there's a conditional to make it work with llhttp. As it is in this commit, this test will not pass.
It's fine on the other release line branches, v10.x and below, because we didn't have to deal with the llhttp split.
Sorry @mcollina
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rvagg I noticed that when I LGTM the releases! Thanks for clarifying!
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there way to override this 8K limit. Our application uses cookie from different apps which make header length more than 8K. With latest build our apps are blowing because of the change. @rvagg @mcollina
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are working on that, see #24811
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @mcollina. what is tentative date of releasing this
1860352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no tentative date, it'll be released when it's ready.