http,https: protect against slow headers attack · nodejs/node@eb43bc0

@@ -37,7 +37,7 @@ const {

3737

_checkInvalidHeaderChar: checkInvalidHeaderChar

3838

} = require('_http_common');

3939

const { OutgoingMessage } = require('_http_outgoing');

40-

const { outHeadersKey, ondrain } = require('internal/http');

40+

const { outHeadersKey, ondrain, nowDate } = require('internal/http');

4141

const {

4242

defaultTriggerAsyncIdScope,

4343

getOrSetAsyncId

@@ -303,6 +303,7 @@ function Server(options, requestListener) {

303303

this.keepAliveTimeout = 5000;

304304

this._pendingResponseData = 0;

305305

this.maxHeadersCount = null;

306+

this.headersTimeout = 40 * 1000; // 40 seconds

306307

}

307308

util.inherits(Server, net.Server);

308309

@@ -341,6 +342,9 @@ function connectionListenerInternal(server, socket) {

341342

var parser = parsers.alloc();

342343

parser.reinitialize(HTTPParser.REQUEST);

343344

parser.socket = socket;

345+346+

// We are starting to wait for our headers.

347+

parser.parsingHeadersStart = nowDate();

344348

socket.parser = parser;

345349346350

// Propagate headers limit from server instance to parser

@@ -478,7 +482,20 @@ function socketOnData(server, socket, parser, state, d) {

478482479483

function onParserExecute(server, socket, parser, state, ret) {

480484

socket._unrefTimer();

485+

const start = parser.parsingHeadersStart;

481486

debug('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+482499

onParserExecuteCommon(server, socket, parser, state, ret, undefined);

483500

}

484501

@@ -589,6 +606,9 @@ function resOnFinish(req, res, socket, state, server) {

589606

function parserOnIncoming(server, socket, state, req, keepAlive) {

590607

resetSocketTimeout(server, socket, state);

591608609+

// Set to zero to communicate that we have finished parsing.

610+

socket.parser.parsingHeadersStart = 0;

611+592612

if (req.upgrade) {

593613

req.upgrade = req.method === 'CONNECT' ||

594614

server.listenerCount('upgrade') > 0;