diff options
author | Matteo Collina <hello@matteocollina.com> | 2018-08-23 16:46:07 +0200 |
---|---|---|
committer | Rod Vagg <rod@vagg.org> | 2018-11-28 11:36:34 +1100 |
commit | ee618a7ab239c98d945c723a4e225bc409151736 (patch) | |
tree | b70be2ea28bb3773d6c455a61a273cf8c5edbfb8 /lib/_http_server.js | |
parent | 7bfcfc2ffe4940898cf7b70890a55eb91cbdd112 (diff) | |
download | android-node-v8-ee618a7ab239c98d945c723a4e225bc409151736.tar.gz android-node-v8-ee618a7ab239c98d945c723a4e225bc409151736.tar.bz2 android-node-v8-ee618a7ab239c98d945c723a4e225bc409151736.zip |
http,https: protect against slow headers attack
CVE-2018-12122
An attacker can send a char/s within headers and exahust the resources
(file descriptors) of a system even with a tight max header length
protection. This PR destroys a socket if it has not received the headers
in 40s.
PR-URL: https://github.com/nodejs-private/node-private/pull/144
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/_http_server.js')
-rw-r--r-- | lib/_http_server.js | 22 |
1 files changed, 21 insertions, 1 deletions
diff --git a/lib/_http_server.js b/lib/_http_server.js index 96f05f5819..c171b1d3e7 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -37,7 +37,7 @@ const { _checkInvalidHeaderChar: checkInvalidHeaderChar } = require('_http_common'); const { OutgoingMessage } = require('_http_outgoing'); -const { outHeadersKey, ondrain } = require('internal/http'); +const { outHeadersKey, ondrain, nowDate } = require('internal/http'); const { defaultTriggerAsyncIdScope, getOrSetAsyncId @@ -306,6 +306,7 @@ function Server(options, requestListener) { this.keepAliveTimeout = 5000; this._pendingResponseData = 0; this.maxHeadersCount = null; + this.headersTimeout = 40 * 1000; // 40 seconds } util.inherits(Server, net.Server); @@ -344,6 +345,9 @@ function connectionListenerInternal(server, socket) { var parser = parsers.alloc(); parser.reinitialize(HTTPParser.REQUEST, parser[is_reused_symbol]); parser.socket = socket; + + // We are starting to wait for our headers. + parser.parsingHeadersStart = nowDate(); socket.parser = parser; // Propagate headers limit from server instance to parser @@ -481,7 +485,20 @@ function socketOnData(server, socket, parser, state, d) { function onParserExecute(server, socket, parser, state, ret) { socket._unrefTimer(); + const start = parser.parsingHeadersStart; debug('SERVER socketOnParserExecute %d', ret); + + // If we have not parsed the headers, destroy the socket + // after server.headersTimeout to protect from DoS attacks. + // start === 0 means that we have parsed headers. + if (start !== 0 && nowDate() - start > server.headersTimeout) { + const serverTimeout = server.emit('timeout', socket); + + if (!serverTimeout) + socket.destroy(); + return; + } + onParserExecuteCommon(server, socket, parser, state, ret, undefined); } @@ -598,6 +615,9 @@ function emitCloseNT(self) { function parserOnIncoming(server, socket, state, req, keepAlive) { resetSocketTimeout(server, socket, state); + // Set to zero to communicate that we have finished parsing. + socket.parser.parsingHeadersStart = 0; + if (req.upgrade) { req.upgrade = req.method === 'CONNECT' || server.listenerCount('upgrade') > 0; |