107 lines
3.1 KiB
Diff
107 lines
3.1 KiB
Diff
From 1a7302bd48593cd95473600b153022dda2b811a1 Mon Sep 17 00:00:00 2001
|
|
From: Matteo Collina <hello@matteocollina.com>
|
|
Date: Sat, 1 Dec 2018 16:29:13 +0100
|
|
Subject: [PATCH] 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/158
|
|
Reviewed-By: Rod Vagg <rod@vagg.org>
|
|
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
|
|
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
|
|
---
|
|
lib/_http_server.js | 15 ++++++
|
|
.../test-http-slow-headers-keepalive.js | 51 +++++++++++++++++++
|
|
2 files changed, 66 insertions(+)
|
|
create mode 100644 test/parallel/test-http-slow-headers-keepalive.js
|
|
|
|
diff --git a/lib/_http_server.js b/lib/_http_server.js
|
|
index 797597aa9f71..2c7c6c29b864 100644
|
|
--- a/lib/_http_server.js
|
|
+++ b/lib/_http_server.js
|
|
@@ -606,6 +606,10 @@ function resOnFinish(req, res, socket, state, server) {
|
|
function parserOnIncoming(server, socket, state, req, keepAlive) {
|
|
resetSocketTimeout(server, socket, state);
|
|
|
|
+ if (server.keepAliveTimeout > 0) {
|
|
+ req.on('end', resetHeadersTimeoutOnReqEnd);
|
|
+ }
|
|
+
|
|
// Set to zero to communicate that we have finished parsing.
|
|
socket.parser.parsingHeadersStart = 0;
|
|
|
|
@@ -730,6 +734,17 @@ function socketOnWrap(ev, fn) {
|
|
return res;
|
|
}
|
|
|
|
+function resetHeadersTimeoutOnReqEnd() {
|
|
+ debug('resetHeadersTimeoutOnReqEnd');
|
|
+
|
|
+ const 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();
|
|
+ }
|
|
+}
|
|
+
|
|
module.exports = {
|
|
STATUS_CODES,
|
|
Server,
|
|
diff --git a/test/parallel/test-http-slow-headers-keepalive.js b/test/parallel/test-http-slow-headers-keepalive.js
|
|
new file mode 100644
|
|
index 000000000000..5552f33f77e3
|
|
--- /dev/null
|
|
+++ b/test/parallel/test-http-slow-headers-keepalive.js
|
|
@@ -0,0 +1,51 @@
|
|
+'use strict';
|
|
+
|
|
+const common = require('../common');
|
|
+const http = require('http');
|
|
+const net = require('net');
|
|
+const { finished } = require('stream');
|
|
+
|
|
+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();
|
|
+ finished(client, common.mustCall((err) => {
|
|
+ clearInterval(interval);
|
|
+ server.close();
|
|
+ }));
|
|
+});
|