fix cve-2019-5737
This commit is contained in:
parent
03699ff666
commit
5934c117d0
208
CVE-2018-12122.patch
Normal file
208
CVE-2018-12122.patch
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
From ee618a7ab239c98d945c723a4e225bc409151736 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Matteo Collina <hello@matteocollina.com>
|
||||||
|
Date: Thu, 23 Aug 2018 16:46:07 +0200
|
||||||
|
Subject: [PATCH] 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>
|
||||||
|
---
|
||||||
|
doc/api/http.md | 20 ++++++++
|
||||||
|
doc/api/https.md | 7 +++
|
||||||
|
lib/_http_server.js | 22 ++++++++-
|
||||||
|
lib/https.js | 1 +
|
||||||
|
lib/internal/http.js | 27 +++++++---
|
||||||
|
test/async-hooks/test-graph.http.js | 2 +-
|
||||||
|
test/parallel/test-http-slow-headers.js | 50 +++++++++++++++++++
|
||||||
|
test/parallel/test-https-slow-headers.js | 63 ++++++++++++++++++++++++
|
||||||
|
8 files changed, 182 insertions(+), 10 deletions(-)
|
||||||
|
create mode 100644 test/parallel/test-http-slow-headers.js
|
||||||
|
create mode 100644 test/parallel/test-https-slow-headers.js
|
||||||
|
|
||||||
|
diff --git a/doc/api/http.md b/doc/api/http.md
|
||||||
|
index 1c6b5717e004..13373debb404 100644
|
||||||
|
--- a/doc/api/http.md
|
||||||
|
+++ b/doc/api/http.md
|
||||||
|
@@ -958,6 +958,26 @@ added: v0.7.0
|
||||||
|
|
||||||
|
Limits maximum incoming headers count. If set to 0, no limit will be applied.
|
||||||
|
|
||||||
|
+### server.headersTimeout
|
||||||
|
+<!-- YAML
|
||||||
|
+added: REPLACEME
|
||||||
|
+-->
|
||||||
|
+
|
||||||
|
+* {number} **Default:** `40000`
|
||||||
|
+
|
||||||
|
+Limit the amount of time the parser will wait to receive the complete HTTP
|
||||||
|
+headers.
|
||||||
|
+
|
||||||
|
+In case of inactivity, the rules defined in [server.timeout][] apply. However,
|
||||||
|
+that inactivity based timeout would still allow the connection to be kept open
|
||||||
|
+if the headers are being sent very slowly (by default, up to a byte per 2
|
||||||
|
+minutes). In order to prevent this, whenever header data arrives an additional
|
||||||
|
+check is made that more than `server.headersTimeout` milliseconds has not
|
||||||
|
+passed since the connection was established. If the check fails, a `'timeout'`
|
||||||
|
+event is emitted on the server object, and (by default) the socket is destroyed.
|
||||||
|
+See [server.timeout][] for more information on how timeout behaviour can be
|
||||||
|
+customised.
|
||||||
|
+
|
||||||
|
### server.setTimeout([msecs][, callback])
|
||||||
|
<!-- YAML
|
||||||
|
added: v0.9.12
|
||||||
|
diff --git a/doc/api/https.md b/doc/api/https.md
|
||||||
|
index 777fbab741c8..81a5bcce934e 100644
|
||||||
|
--- a/doc/api/https.md
|
||||||
|
+++ b/doc/api/https.md
|
||||||
|
@@ -44,6 +44,12 @@ This method is identical to [`server.listen()`][] from [`net.Server`][].
|
||||||
|
|
||||||
|
See [`http.Server#maxHeadersCount`][].
|
||||||
|
|
||||||
|
+### server.headersTimeout
|
||||||
|
+
|
||||||
|
+- {number} **Default:** `40000`
|
||||||
|
+
|
||||||
|
+See [`http.Server#headersTimeout`][].
|
||||||
|
+
|
||||||
|
### server.setTimeout([msecs][, callback])
|
||||||
|
<!-- YAML
|
||||||
|
added: v0.11.2
|
||||||
|
@@ -363,6 +369,7 @@ headers: max-age=0; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18="; p
|
||||||
|
[`http.Agent`]: http.html#http_class_http_agent
|
||||||
|
[`http.Server#keepAliveTimeout`]: http.html#http_server_keepalivetimeout
|
||||||
|
[`http.Server#maxHeadersCount`]: http.html#http_server_maxheaderscount
|
||||||
|
+[`http.Server#headersTimeout`]: http.html#http_server_headerstimeout
|
||||||
|
[`http.Server#setTimeout()`]: http.html#http_server_settimeout_msecs_callback
|
||||||
|
[`http.Server#timeout`]: http.html#http_server_timeout
|
||||||
|
[`http.Server`]: http.html#http_class_http_server
|
||||||
|
diff --git a/lib/_http_server.js b/lib/_http_server.js
|
||||||
|
index 96f05f5819d3..c171b1d3e78a 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.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;
|
||||||
|
diff --git a/lib/https.js b/lib/https.js
|
||||||
|
index 66e76c1f0509..0854c3d44057 100644
|
||||||
|
--- a/lib/https.js
|
||||||
|
+++ b/lib/https.js
|
||||||
|
@@ -74,6 +74,7 @@ function Server(opts, requestListener) {
|
||||||
|
this.timeout = 2 * 60 * 1000;
|
||||||
|
this.keepAliveTimeout = 5000;
|
||||||
|
this.maxHeadersCount = null;
|
||||||
|
+ this.headersTimeout = 40 * 1000; // 40 seconds
|
||||||
|
}
|
||||||
|
inherits(Server, tls.Server);
|
||||||
|
|
||||||
|
diff --git a/lib/internal/http.js b/lib/internal/http.js
|
||||||
|
index 2b9c948aeefb..47a51fb73949 100644
|
||||||
|
--- a/lib/internal/http.js
|
||||||
|
+++ b/lib/internal/http.js
|
||||||
|
@@ -2,19 +2,29 @@
|
||||||
|
|
||||||
|
const { setUnrefTimeout } = require('internal/timers');
|
||||||
|
|
||||||
|
-var dateCache;
|
||||||
|
+var nowCache;
|
||||||
|
+var utcCache;
|
||||||
|
+
|
||||||
|
+function nowDate() {
|
||||||
|
+ if (!nowCache) cache();
|
||||||
|
+ return nowCache;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
function utcDate() {
|
||||||
|
- if (!dateCache) {
|
||||||
|
- const d = new Date();
|
||||||
|
- dateCache = d.toUTCString();
|
||||||
|
+ if (!utcCache) cache();
|
||||||
|
+ return utcCache;
|
||||||
|
+}
|
||||||
|
|
||||||
|
- setUnrefTimeout(resetCache, 1000 - d.getMilliseconds());
|
||||||
|
- }
|
||||||
|
- return dateCache;
|
||||||
|
+function cache() {
|
||||||
|
+ const d = new Date();
|
||||||
|
+ nowCache = d.valueOf();
|
||||||
|
+ utcCache = d.toUTCString();
|
||||||
|
+ setUnrefTimeout(resetCache, 1000 - d.getMilliseconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetCache() {
|
||||||
|
- dateCache = undefined;
|
||||||
|
+ nowCache = undefined;
|
||||||
|
+ utcCache = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ondrain() {
|
||||||
|
@@ -24,5 +34,6 @@ function ondrain() {
|
||||||
|
module.exports = {
|
||||||
|
outHeadersKey: Symbol('outHeadersKey'),
|
||||||
|
ondrain,
|
||||||
|
+ nowDate,
|
||||||
|
utcDate
|
||||||
|
};
|
||||||
106
CVE-2019-5737.patch
Normal file
106
CVE-2019-5737.patch
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
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();
|
||||||
|
+ }));
|
||||||
|
+});
|
||||||
15
nodejs.spec
15
nodejs.spec
@ -37,8 +37,12 @@ Source0: node-v%{nodejs_version}-stripped.tar.gz
|
|||||||
Source1: nodejs_native.attr
|
Source1: nodejs_native.attr
|
||||||
Source2: nodejs_native.req
|
Source2: nodejs_native.req
|
||||||
|
|
||||||
Patch1: Disable-running-gyp-on-shared-deps.patch
|
Patch0001: Disable-running-gyp-on-shared-deps.patch
|
||||||
Patch2: Suppress-NPM-message-to-run-global-update.patch
|
Patch0002: Suppress-NPM-message-to-run-global-update.patch
|
||||||
|
#https://github.com/nodejs/node/commit/ee618a7ab239c98d945c723a4e225bc409151736
|
||||||
|
Patch0003: CVE-2018-12122.patch
|
||||||
|
#https://github.com/nodejs/node/commit/1a7302bd48
|
||||||
|
Patch0004: CVE-2019-5737.patch
|
||||||
|
|
||||||
BuildRequires: gcc gcc-c++ openssl-devel
|
BuildRequires: gcc gcc-c++ openssl-devel
|
||||||
BuildRequires: http-parser-devel
|
BuildRequires: http-parser-devel
|
||||||
@ -97,9 +101,7 @@ BuildArch: noarch
|
|||||||
The manual documentation for Nodejs.
|
The manual documentation for Nodejs.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup -q -n node-v%{nodejs_version}
|
%autosetup -n node-v%{nodejs_version} -p1
|
||||||
%patch1 -p1
|
|
||||||
%patch2 -p1
|
|
||||||
|
|
||||||
pathfix.py -i %{__python2} -pn $(find -type f)
|
pathfix.py -i %{__python2} -pn $(find -type f)
|
||||||
find . -type f -exec sed -i "s~/usr\/bin\/env python~/usr/bin/python2~" {} \;
|
find . -type f -exec sed -i "s~/usr\/bin\/env python~/usr/bin/python2~" {} \;
|
||||||
@ -217,5 +219,8 @@ NODE_PATH=%{buildroot}%{_prefix}/lib/node_modules:%{buildroot}%{_prefix}/lib/nod
|
|||||||
%doc %{_mandir}/man1/node.1*
|
%doc %{_mandir}/man1/node.1*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Mar 20 2020 shijian <shijian16@huawei.com> - 1:10.11.0-2
|
||||||
|
- Fix CVE-2018-12122 CVE-2019-5737
|
||||||
|
|
||||||
* Fri Mar 6 2020 openEuler Buildteam <buildteam@openeuler.org> - 1:10.11.0-1
|
* Fri Mar 6 2020 openEuler Buildteam <buildteam@openeuler.org> - 1:10.11.0-1
|
||||||
- Package init
|
- Package init
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user