Skip to content

Commit 5fef5c6

Browse files
jasnellrvagg
authored andcommitted
tls: add --tls-cipher-list command line switch
This adds a new `--tls-cipher-list` command line switch that can be used to override the built-in default cipher list. The intent of this is to make it possible to enforce an alternative default cipher list at the process level. Overriding the default cipher list is still permitted at the application level by changing the value of `require('tls').DEFAULT_CIPHERS`. As part of the change, the built in default list is moved out of tls.js and into node_constants.h and node_constants.cc. Two new constants are added to require('constants'): * defaultCipherList (the active default cipher list) * defaultCoreCipherList (the built-in default cipher list) A test case and doc changes are included. A new NODE_DEFINE_STRING_CONSTANT macro is also created in node_internals.h When node_constants is initialized, it will pick up either the passed in command line switch or fallback to the default built-in suite. Within joyent/node, this change had originaly been wrapped up with a number of other related commits involving the removal of the RC4 cipher. This breaks out this isolated change. /cc @mhdawson, @misterdjules, @trevnorris, @indutny, @rvagg Reviewed By: Ben Noordhuis <[email protected]> PR-URL: #2412
1 parent c9867fe commit 5fef5c6

File tree

8 files changed

+144
-24
lines changed

8 files changed

+144
-24
lines changed

doc/api/tls.markdown

+39-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,44 @@ handshake extensions allowing you:
7777
* SNI - to use one TLS server for multiple hostnames with different SSL
7878
certificates.
7979

80+
## Modifying the Default TLS Cipher suite
81+
82+
Node.js is built with a default suite of enabled and disabled TLS ciphers.
83+
Currently, the default cipher suite is:
84+
85+
ECDHE-RSA-AES128-GCM-SHA256:
86+
ECDHE-ECDSA-AES128-GCM-SHA256:
87+
ECDHE-RSA-AES256-GCM-SHA384:
88+
ECDHE-ECDSA-AES256-GCM-SHA384:
89+
DHE-RSA-AES128-GCM-SHA256:
90+
ECDHE-RSA-AES128-SHA256:
91+
DHE-RSA-AES128-SHA256:
92+
ECDHE-RSA-AES256-SHA384:
93+
DHE-RSA-AES256-SHA384:
94+
ECDHE-RSA-AES256-SHA256:
95+
DHE-RSA-AES256-SHA256:
96+
HIGH:
97+
!aNULL:
98+
!eNULL:
99+
!EXPORT:
100+
!DES:
101+
!RC4:
102+
!MD5:
103+
!PSK:
104+
!SRP:
105+
!CAMELLIA
106+
107+
This default can be overriden entirely using the `--tls-cipher-list` command
108+
line switch. For instance, the following makes
109+
`ECDHE-RSA-AES128-GCM-SHA256:!RC4` the default TLS cipher suite:
110+
111+
node --tls-cipher-list="ECDHE-RSA-AES128-GCM-SHA256:!RC4"
112+
113+
Note that the default cipher suite included within Node.js has been carefully
114+
selected to reflect current security best practices and risk mitigation.
115+
Changing the default cipher suite can have a significant impact on the security
116+
of an application. The `--tls-cipher-list` switch should by used only if
117+
absolutely necessary.
80118

81119
## Perfect Forward Secrecy
82120

@@ -138,7 +176,7 @@ automatically set as a listener for the [secureConnection][] event. The
138176
- `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate
139177
Revocation List)
140178

141-
- `ciphers`: A string describing the ciphers to use or exclude, seperated by
179+
- `ciphers`: A string describing the ciphers to use or exclude, separated by
142180
`:`. The default cipher suite is:
143181

144182
ECDHE-RSA-AES128-GCM-SHA256:

doc/iojs.1

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ and servers.
6464

6565
--v8-options print v8 command line options
6666

67+
--tls-cipher-list=list use an alternative default TLS cipher list
68+
(available only when Node.js is built with
69+
OpenSSL and crypto support enabled)
6770

6871
.SH ENVIRONMENT VARIABLES
6972

lib/tls.js

+2-23
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const url = require('url');
55
const util = require('util');
66
const binding = process.binding('crypto');
77
const Buffer = require('buffer').Buffer;
8+
const constants = require('constants');
89

910
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
1011
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
@@ -15,29 +16,7 @@ exports.CLIENT_RENEG_WINDOW = 600;
1516

1617
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
1718

18-
exports.DEFAULT_CIPHERS = [
19-
'ECDHE-RSA-AES128-GCM-SHA256',
20-
'ECDHE-ECDSA-AES128-GCM-SHA256',
21-
'ECDHE-RSA-AES256-GCM-SHA384',
22-
'ECDHE-ECDSA-AES256-GCM-SHA384',
23-
'DHE-RSA-AES128-GCM-SHA256',
24-
'ECDHE-RSA-AES128-SHA256',
25-
'DHE-RSA-AES128-SHA256',
26-
'ECDHE-RSA-AES256-SHA384',
27-
'DHE-RSA-AES256-SHA384',
28-
'ECDHE-RSA-AES256-SHA256',
29-
'DHE-RSA-AES256-SHA256',
30-
'HIGH',
31-
'!aNULL',
32-
'!eNULL',
33-
'!EXPORT',
34-
'!DES',
35-
'!RC4',
36-
'!MD5',
37-
'!PSK',
38-
'!SRP',
39-
'!CAMELLIA'
40-
].join(':');
19+
exports.DEFAULT_CIPHERS = constants.defaultCipherList;
4120

4221
exports.DEFAULT_ECDH_CURVE = 'prime256v1';
4322

src/node.cc

+7
Original file line numberDiff line numberDiff line change
@@ -3111,6 +3111,9 @@ static void PrintHelp() {
31113111
" --track-heap-objects track heap object allocations for heap "
31123112
"snapshots\n"
31133113
" --v8-options print v8 command line options\n"
3114+
#if HAVE_OPENSSL
3115+
" --tls-cipher-list=val use an alternative default TLS cipher list\n"
3116+
#endif
31143117
#if defined(NODE_HAVE_I18N_SUPPORT)
31153118
" --icu-data-dir=dir set ICU data load path to dir\n"
31163119
" (overrides NODE_ICU_DATA)\n"
@@ -3242,6 +3245,10 @@ static void ParseArgs(int* argc,
32423245
} else if (strcmp(arg, "--v8-options") == 0) {
32433246
new_v8_argv[new_v8_argc] = "--help";
32443247
new_v8_argc += 1;
3248+
#if HAVE_OPENSSL
3249+
} else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) {
3250+
default_cipher_list = arg + 18;
3251+
#endif
32453252
#if defined(NODE_HAVE_I18N_SUPPORT)
32463253
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
32473254
icu_data_dir = arg + 15;

src/node_constants.cc

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ namespace node {
2424
using v8::Handle;
2525
using v8::Object;
2626

27+
#if HAVE_OPENSSL
28+
const char* default_cipher_list = DEFAULT_CIPHER_LIST_CORE;
29+
#endif
30+
2731
void DefineErrnoConstants(Handle<Object> target) {
2832
#ifdef E2BIG
2933
NODE_DEFINE_CONSTANT(target, E2BIG);
@@ -1108,13 +1112,25 @@ void DefineUVConstants(Handle<Object> target) {
11081112
NODE_DEFINE_CONSTANT(target, UV_UDP_REUSEADDR);
11091113
}
11101114

1115+
void DefineCryptoConstants(Handle<Object> target) {
1116+
#if HAVE_OPENSSL
1117+
NODE_DEFINE_STRING_CONSTANT(target,
1118+
"defaultCoreCipherList",
1119+
DEFAULT_CIPHER_LIST_CORE);
1120+
NODE_DEFINE_STRING_CONSTANT(target,
1121+
"defaultCipherList",
1122+
default_cipher_list);
1123+
#endif
1124+
}
1125+
11111126
void DefineConstants(Handle<Object> target) {
11121127
DefineErrnoConstants(target);
11131128
DefineWindowsErrorConstants(target);
11141129
DefineSignalConstants(target);
11151130
DefineOpenSSLConstants(target);
11161131
DefineSystemConstants(target);
11171132
DefineUVConstants(target);
1133+
DefineCryptoConstants(target);
11181134
}
11191135

11201136
} // namespace node

src/node_constants.h

+29
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,36 @@
44
#include "node.h"
55
#include "v8.h"
66

7+
#if HAVE_OPENSSL
8+
#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \
9+
"ECDHE-ECDSA-AES128-GCM-SHA256:" \
10+
"ECDHE-RSA-AES256-GCM-SHA384:" \
11+
"ECDHE-ECDSA-AES256-GCM-SHA384:" \
12+
"DHE-RSA-AES128-GCM-SHA256:" \
13+
"ECDHE-RSA-AES128-SHA256:" \
14+
"DHE-RSA-AES128-SHA256:" \
15+
"ECDHE-RSA-AES256-SHA384:" \
16+
"DHE-RSA-AES256-SHA384:" \
17+
"ECDHE-RSA-AES256-SHA256:" \
18+
"DHE-RSA-AES256-SHA256:" \
19+
"HIGH:" \
20+
"!aNULL:" \
21+
"!eNULL:" \
22+
"!EXPORT:" \
23+
"!DES:" \
24+
"!RC4:" \
25+
"!MD5:" \
26+
"!PSK:" \
27+
"!SRP:" \
28+
"!CAMELLIA"
29+
#endif
30+
731
namespace node {
32+
33+
#if HAVE_OPENSSL
34+
extern const char* default_cipher_list;
35+
#endif
36+
837
void DefineConstants(v8::Handle<v8::Object> target);
938
} // namespace node
1039

src/node_internals.h

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@
1212

1313
struct sockaddr;
1414

15+
// Variation on NODE_DEFINE_CONSTANT that sets a String value.
16+
#define NODE_DEFINE_STRING_CONSTANT(target, name, constant) \
17+
do { \
18+
v8::Isolate* isolate = target->GetIsolate(); \
19+
v8::Local<v8::String> constant_name = \
20+
v8::String::NewFromUtf8(isolate, name); \
21+
v8::Local<v8::String> constant_value = \
22+
v8::String::NewFromUtf8(isolate, constant); \
23+
v8::PropertyAttribute constant_attributes = \
24+
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); \
25+
target->ForceSet(isolate->GetCurrentContext(), \
26+
constant_name, \
27+
constant_value, \
28+
constant_attributes); \
29+
} while (0)
30+
1531
namespace node {
1632

1733
// Forward declaration

test/parallel/test-tls-cipher-list.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
if (!common.hasCrypto) {
5+
console.log('1..0 # Skipped: missing crypto');
6+
return;
7+
}
8+
9+
const assert = require('assert');
10+
const spawn = require('child_process').spawn;
11+
const defaultCoreList = require('constants').defaultCoreCipherList;
12+
13+
function doCheck(arg, check) {
14+
var out = '';
15+
var arg = arg.concat([
16+
'-pe',
17+
'require("constants").defaultCipherList'
18+
]);
19+
spawn(process.execPath, arg, {}).
20+
on('error', assert.fail).
21+
stdout.on('data', function(chunk) {
22+
out += chunk;
23+
}).on('end', function() {
24+
assert.equal(out.trim(), check);
25+
}).on('error', assert.fail);
26+
}
27+
28+
// test the default unmodified version
29+
doCheck([], defaultCoreList);
30+
31+
// test the command line switch by itself
32+
doCheck(['--tls-cipher-list=ABC'], 'ABC');

0 commit comments

Comments
 (0)