http,https: protect against slow headers attack · nodejs/node@eb43bc0
@@ -37,7 +37,7 @@ const {
3737_checkInvalidHeaderChar: checkInvalidHeaderChar
3838} = require('_http_common');
3939const { OutgoingMessage } = require('_http_outgoing');
40-const { outHeadersKey, ondrain } = require('internal/http');
40+const { outHeadersKey, ondrain, nowDate } = require('internal/http');
4141const {
4242 defaultTriggerAsyncIdScope,
4343 getOrSetAsyncId
@@ -303,6 +303,7 @@ function Server(options, requestListener) {
303303this.keepAliveTimeout = 5000;
304304this._pendingResponseData = 0;
305305this.maxHeadersCount = null;
306+this.headersTimeout = 40 * 1000; // 40 seconds
306307}
307308util.inherits(Server, net.Server);
308309@@ -341,6 +342,9 @@ function connectionListenerInternal(server, socket) {
341342var parser = parsers.alloc();
342343parser.reinitialize(HTTPParser.REQUEST);
343344parser.socket = socket;
345+346+// We are starting to wait for our headers.
347+parser.parsingHeadersStart = nowDate();
344348socket.parser = parser;
345349346350// Propagate headers limit from server instance to parser
@@ -478,7 +482,20 @@ function socketOnData(server, socket, parser, state, d) {
478482479483function onParserExecute(server, socket, parser, state, ret) {
480484socket._unrefTimer();
485+const start = parser.parsingHeadersStart;
481486debug('SERVER socketOnParserExecute %d', ret);
487+488+// If we have not parsed the headers, destroy the socket
489+// after server.headersTimeout to protect from DoS attacks.
490+// start === 0 means that we have parsed headers.
491+if (start !== 0 && nowDate() - start > server.headersTimeout) {
492+const serverTimeout = server.emit('timeout', socket);
493+494+if (!serverTimeout)
495+socket.destroy();
496+return;
497+}
498+482499onParserExecuteCommon(server, socket, parser, state, ret, undefined);
483500}
484501@@ -589,6 +606,9 @@ function resOnFinish(req, res, socket, state, server) {
589606function parserOnIncoming(server, socket, state, req, keepAlive) {
590607resetSocketTimeout(server, socket, state);
591608609+// Set to zero to communicate that we have finished parsing.
610+socket.parser.parsingHeadersStart = 0;
611+592612if (req.upgrade) {
593613req.upgrade = req.method === 'CONNECT' ||
594614server.listenerCount('upgrade') > 0;