summaryrefslogtreecommitdiff
path: root/lib/_http_server.js
diff options
context:
space:
mode:
authorMatteo Collina <hello@matteocollina.com>2018-08-23 16:46:07 +0200
committerRod Vagg <rod@vagg.org>2018-11-28 11:36:34 +1100
commitee618a7ab239c98d945c723a4e225bc409151736 (patch)
treeb70be2ea28bb3773d6c455a61a273cf8c5edbfb8 /lib/_http_server.js
parent7bfcfc2ffe4940898cf7b70890a55eb91cbdd112 (diff)
downloadandroid-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.js22
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;