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/162
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 06a208d316
commit b13b4a9ffb
2 changed files with 73 additions and 0 deletions

View File

@ -489,6 +489,10 @@ function connectionListener(socket) {
incoming.push(req);
if (self.keepAliveTimeout > 0) {
req.on('end', resetHeadersTimeoutOnReqEnd);
}
// Set to zero to communicate that we have finished parsing.
socket.parser.parsingHeadersStart = 0;
@ -640,3 +644,14 @@ function socketOnWrap(ev, fn) {
return res;
}
function resetHeadersTimeoutOnReqEnd() {
debug('resetHeadersTimeoutOnReqEnd');
var 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();
}
}

View File

@ -0,0 +1,58 @@
'use strict';
const common = require('../common');
const http = require('http');
const net = require('net');
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();
const onClose = common.mustCall(() => {
client.removeListener('close', onClose);
client.removeListener('error', onClose);
client.removeListener('end', onClose);
clearInterval(interval);
server.close();
});
client.on('error', onClose);
client.on('close', onClose);
client.on('end', onClose);
});