http: prevent slowloris with keepalive connections

Fixes: https://github.com/nodejs-private/security/issues/214
PR-URL: https://github.com/nodejs-private/node-private/pull/158
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
This commit is contained in:
Matteo Collina 2018-12-01 16:29:13 +01:00 committed by Rod Vagg
parent 04d5c1b8fd
commit 667b31be95
2 changed files with 66 additions and 0 deletions

View File

@ -622,6 +622,10 @@ function emitCloseNT(self) {
function parserOnIncoming(server, socket, state, req, keepAlive) {
resetSocketTimeout(server, socket, state);
if (server.keepAliveTimeout > 0) {
req.on('end', resetHeadersTimeoutOnReqEnd);
}
// Set to zero to communicate that we have finished parsing.
socket.parser.parsingHeadersStart = 0;
@ -745,6 +749,17 @@ function socketOnWrap(ev, fn) {
return res;
}
function resetHeadersTimeoutOnReqEnd() {
debug('resetHeadersTimeoutOnReqEnd');
const parser = this.socket.parser;
// Parser can be null if the socket was destroyed
// in that case, there is nothing to do.
if (parser !== null) {
parser.parsingHeadersStart = nowDate();
}
}
module.exports = {
STATUS_CODES,
Server,

View File

@ -0,0 +1,51 @@
'use strict';
const common = require('../common');
const http = require('http');
const net = require('net');
const { finished } = require('stream');
const headers =
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Connection: keep-alive' +
'Agent: node\r\n';
let sendCharEvery = 1000;
const server = http.createServer(common.mustCall((req, res) => {
res.writeHead(200);
res.end();
}));
// Pass a REAL env variable to shortening up the default
// value which is 40s otherwise this is useful for manual
// testing
if (!process.env.REAL) {
sendCharEvery = common.platformTimeout(10);
server.headersTimeout = 2 * sendCharEvery;
}
server.once('timeout', common.mustCall((socket) => {
socket.destroy();
}));
server.listen(0, () => {
const client = net.connect(server.address().port);
client.write(headers);
// finish the first request
client.write('\r\n');
// second request
client.write(headers);
client.write('X-CRASH: ');
const interval = setInterval(() => {
client.write('a');
}, sendCharEvery);
client.resume();
finished(client, common.mustCall((err) => {
clearInterval(interval);
server.close();
}));
});