Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit 008ccd9

Browse files
committed
tls: command-line switch and envar cipher-list override
Add command line switches and environment variables to override the default cipher suite in tls.js `--cipher-list` and `NODE_CIPHER_LIST` can be used to completely override the default cipher list with a given value. `--enable-legacy-cipher-list` and `NODE_LEGACY_CIPHER_LIST` can be used to reset the default cipher list back to a known legacy value shipped in prior Node.js releases A new `getLegacyCiphers` method on the tis module allows programmatic access to the old cipher list defaults.
1 parent 1b1fe53 commit 008ccd9

File tree

7 files changed

+274
-19
lines changed

7 files changed

+274
-19
lines changed

doc/api/tls.markdown

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,81 @@ the character "E" appended to the traditional abbreviations):
136136
Ephemeral methods may have some performance drawbacks, because key generation
137137
is expensive.
138138

139+
## Modifying the Default Cipher Suite
140+
141+
Node.js is built with a default suite of enabled and disabled ciphers.
142+
Currently, the default cipher suite is:
143+
144+
ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:
145+
DHE-RSA-AES256-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:
146+
HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA
147+
148+
This default can be overridden entirely using the `--cipher-list` command line
149+
switch or `NODE_CIPHER_LIST` environment variable. For instance:
150+
151+
node --cipher-list=ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384
152+
153+
Setting the environment variable would have the same effect:
154+
155+
NODE_CIPHER_LIST=ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384
156+
157+
CAUTION: The default cipher suite has been carefully selected to reflect current
158+
security best practices and risk mitigation. Changing the default cipher suite
159+
can have a significant impact on the security of an application. The
160+
`--cipher-list` and `NODE_CIPHER_LIST` options should only be used if
161+
absolutely necessary.
162+
163+
### Using Legacy Default Cipher Suite ###
164+
165+
It is possible for the built-in default cipher suite to change from one release
166+
of Node.js to another. For instance, v0.10.38 uses a different default than
167+
v0.12.2. Such changes can cause issues with applications written to assume
168+
certain specific defaults. To help buffer applications against such changes,
169+
the `--enable-legacy-cipher-list` command line switch or `NODE_LEGACY_CIPHER_LIST`
170+
environment variable can be set to specify a specific preset default:
171+
172+
# Use the v0.10.38 defaults
173+
node --enable-legacy-cipher-list=v0.10.38
174+
// or
175+
NODE_LEGACY_CIPHER_LIST=v0.10.38
176+
177+
# Use the v0.12.2 defaults
178+
node --enable-legacy-cipher-list=v0.12.2
179+
// or
180+
NODE_LEGACY_CIPHER_LIST=v0.12.2
181+
182+
Currently, the values supported for the `enable-legacy-cipher-list` switch and
183+
`NODE_LEGACY_CIPHER_LIST` environment variable include:
184+
185+
v0.10.38 - To enable the default cipher suite used in v0.10.38
186+
187+
ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
188+
189+
v0.10.39 - To enable the default cipher suite used in v0.10.39
190+
191+
ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH
192+
193+
v0.12.2 - To enable the default cipher suite used in v0.12.2
194+
195+
ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:
196+
HIGH:!MD5:!aNULL
197+
198+
v.0.12.3 - To enable the default cipher suite used in v0.12.3
199+
200+
ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:
201+
!RC4:!MD5:!aNULL
202+
203+
These legacy cipher suites are also made available for use via the
204+
`getLegacyCiphers()` method:
205+
206+
var tls = require('tls');
207+
console.log(tls.getLegacyCiphers('v0.10.38'));
208+
209+
CAUTION: Changes to the default cipher suite are typically made in order to
210+
strengthen the default security for applications running within Node.js.
211+
Reverting back to the defaults used by older releases can weaken the security
212+
of your applications. The legacy cipher suites should only be used if absolutely
213+
necessary.
139214

140215
## tls.getCiphers()
141216

@@ -146,6 +221,12 @@ Example:
146221
var ciphers = tls.getCiphers();
147222
console.log(ciphers); // ['AES128-SHA', 'AES256-SHA', ...]
148223

224+
## tls.getLegacyCiphers(version)
225+
226+
Returns the legacy default cipher suite for the specified Node.js release.
227+
228+
Example:
229+
var cipher_suite = tls.getLegacyCiphers('v0.10.38');
149230

150231
## tls.createServer(options[, secureConnectionListener])
151232

lib/tls.js

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
'use strict';
2323

24+
var _crypto = process.binding('crypto');
25+
2426
var net = require('net');
2527
var url = require('url');
2628
var util = require('util');
@@ -35,29 +37,14 @@ exports.CLIENT_RENEG_WINDOW = 600;
3537

3638
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
3739

38-
exports.DEFAULT_CIPHERS = [
39-
'ECDHE-RSA-AES256-SHA384',
40-
'DHE-RSA-AES256-SHA384',
41-
'ECDHE-RSA-AES256-SHA256',
42-
'DHE-RSA-AES256-SHA256',
43-
'ECDHE-RSA-AES128-SHA256',
44-
'DHE-RSA-AES128-SHA256',
45-
'HIGH',
46-
'!aNULL',
47-
'!eNULL',
48-
'!EXPORT',
49-
'!DES',
50-
'!RC4',
51-
'!MD5',
52-
'!PSK',
53-
'!SRP',
54-
'!CAMELLIA'
55-
].join(':');
40+
exports.DEFAULT_CIPHERS = _crypto.DEFAULT_CIPHER_LIST;
5641

5742
exports.DEFAULT_ECDH_CURVE = 'prime256v1';
5843

44+
exports.getLegacyCiphers = _crypto.getLegacyCiphers;
45+
5946
exports.getCiphers = function() {
60-
var names = process.binding('crypto').getSSLCiphers();
47+
var names = _crypto.getSSLCiphers();
6148
// Drop all-caps names in favor of their lowercase aliases,
6249
var ctx = {};
6350
names.forEach(function(name) {

src/node.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,6 +2929,12 @@ static void PrintHelp() {
29292929
#endif
29302930
" --enable-ssl2 enable ssl2\n"
29312931
" --enable-ssl3 enable ssl3\n"
2932+
" --cipher-list=val specify the default TLS cipher list\n"
2933+
" --enable-legacy-cipher-list=val \n"
2934+
" set to v0.10.38 to use the v0.10.38 list,\n"
2935+
" set to v0.10.39 to use the v0.10.39 list.\n"
2936+
" set to v0.12.2 to use the v0.12.2 list.\n"
2937+
" set to v0.12.3 to use the v0.12.3 list.\n"
29322938
"\n"
29332939
"Environment variables:\n"
29342940
#ifdef _WIN32
@@ -2946,6 +2952,12 @@ static void PrintHelp() {
29462952
" (will extend linked-in data)\n"
29472953
#endif
29482954
#endif
2955+
"NODE_CIPHER_LIST Override the default TLS cipher list\n"
2956+
"NODE_LEGACY_CIPHER_LIST\n"
2957+
" Set to v0.10.38 to use the v0.10.38 list,\n"
2958+
" Set to v0.10.39 to use the v0.10.39 list.\n"
2959+
" Set to v0.12.2 to use the v0.12.2 list.\n"
2960+
" Set to v0.12.3 to use the v0.12.3 list.\n"
29492961
"\n"
29502962
"Documentation can be found at http://nodejs.org/\n");
29512963
}
@@ -2985,6 +2997,7 @@ static void ParseArgs(int* argc,
29852997
unsigned int new_argc = 1;
29862998
new_v8_argv[0] = argv[0];
29872999
new_argv[0] = argv[0];
3000+
bool using_legacy_cipher_list = false;
29883001

29893002
unsigned int index = 1;
29903003
while (index < nargs && argv[index][0] == '-') {
@@ -3040,6 +3053,17 @@ static void ParseArgs(int* argc,
30403053
} else if (strcmp(arg, "--v8-options") == 0) {
30413054
new_v8_argv[new_v8_argc] = "--help";
30423055
new_v8_argc += 1;
3056+
} else if (strncmp(arg, "--cipher-list=", 14) == 0) {
3057+
if (!using_legacy_cipher_list) {
3058+
DEFAULT_CIPHER_LIST = arg + 14;
3059+
}
3060+
} else if (strncmp(arg, "--enable-legacy-cipher-list=", 28) == 0) {
3061+
using_legacy_cipher_list = true;
3062+
// use the original v0.10.x/v0.12.x cipher lists
3063+
const char * legacy_list = legacy_cipher_list(arg+28);
3064+
if (legacy_list != NULL) {
3065+
DEFAULT_CIPHER_LIST = legacy_list;
3066+
}
30433067
#if defined(NODE_HAVE_I18N_SUPPORT)
30443068
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
30453069
icu_data_dir = arg + 15;
@@ -3402,6 +3426,23 @@ void Init(int* argc,
34023426
}
34033427
}
34043428

3429+
// if parseargs didn't come up with a cipher_list,
3430+
// try the NODE_CIPHER_LIST env variable to grab the
3431+
// list.
3432+
const char * cipher_list = getenv("NODE_CIPHER_LIST");
3433+
if (cipher_list != NULL) {
3434+
DEFAULT_CIPHER_LIST = cipher_list;
3435+
}
3436+
// Allow the NODE_LEGACY_CIPHER_LIST envar to override the other
3437+
// cipher list options. NODE_LEGACY_CIPHER_LIST=v0.10.x will use
3438+
// the cipher list from v0.10.x, NODE_LEGACY_CIPHER_LIST=v0.12.x will
3439+
// use the cipher list from v0.12.x
3440+
const char * leg_cipher_list =
3441+
legacy_cipher_list(getenv("NODE_LEGACY_CIPHER_LIST"));
3442+
if (leg_cipher_list != NULL) {
3443+
DEFAULT_CIPHER_LIST = leg_cipher_list;
3444+
}
3445+
34053446
#if defined(NODE_HAVE_I18N_SUPPORT)
34063447
if (icu_data_dir == NULL) {
34073448
// if the parameter isn't given, use the env variable.

src/node.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,18 @@ NODE_EXTERN void RunAtExit(Environment* env);
223223
} \
224224
while (0)
225225

226+
#define NODE_DEFINE_STRING_CONSTANT(target, constant) \
227+
do { \
228+
v8::Isolate* isolate = v8::Isolate::GetCurrent(); \
229+
v8::Local<v8::String> constant_name = \
230+
v8::String::NewFromUtf8(isolate, #constant); \
231+
v8::Local<v8::String> constant_value = \
232+
v8::String::NewFromUtf8(isolate, constant); \
233+
v8::PropertyAttribute constant_attributes = \
234+
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); \
235+
(target)->ForceSet(constant_name, constant_value, constant_attributes); \
236+
} while (0)
237+
226238
// Used to be a macro, hence the uppercase name.
227239
template <typename TypeName>
228240
inline void NODE_SET_METHOD(const TypeName& recv,

src/node_crypto.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ namespace node {
7777

7878
bool SSL2_ENABLE = false;
7979
bool SSL3_ENABLE = false;
80+
const char * DEFAULT_CIPHER_LIST = DEFAULT_CIPHER_LIST_HEAD;
8081

8182
namespace crypto {
8283

@@ -4851,6 +4852,26 @@ static void array_push_back(const TypeName* md,
48514852
ctx->arr->Set(ctx->arr->Length(), OneByteString(ctx->env()->isolate(), from));
48524853
}
48534854

4855+
// borrowed from v8
4856+
// (see http://v8.googlecode.com/svn/trunk/samples/shell.cc)
4857+
const char* ToCString(const String::Utf8Value& value) {
4858+
return *value ? *value : "<string conversion failed>";
4859+
}
4860+
4861+
void DefaultCiphers(const v8::FunctionCallbackInfo<v8::Value>& args) {
4862+
Environment* env = Environment::GetCurrent(args.GetIsolate());
4863+
HandleScope scope(env->isolate());
4864+
v8::String::Utf8Value key(args[0]);
4865+
const char * list = legacy_cipher_list(ToCString(key));
4866+
if (list != NULL) {
4867+
args.GetReturnValue().Set(
4868+
v8::String::NewFromUtf8(args.GetIsolate(), list));
4869+
} else {
4870+
args.GetReturnValue().Set(
4871+
v8::String::NewFromUtf8(args.GetIsolate(),
4872+
DEFAULT_CIPHER_LIST_HEAD));
4873+
}
4874+
}
48544875

48554876
void GetCiphers(const FunctionCallbackInfo<Value>& args) {
48564877
Environment* env = Environment::GetCurrent(args.GetIsolate());
@@ -5171,6 +5192,8 @@ void InitCrypto(Handle<Object> target,
51715192

51725193
NODE_DEFINE_CONSTANT(target, SSL3_ENABLE);
51735194
NODE_DEFINE_CONSTANT(target, SSL2_ENABLE);
5195+
NODE_DEFINE_STRING_CONSTANT(target, DEFAULT_CIPHER_LIST);
5196+
NODE_SET_METHOD(target, "getLegacyCiphers", DefaultCiphers);
51745197
}
51755198

51765199
} // namespace crypto

src/node_crypto.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#include "v8.h"
4040

41+
#include <string.h>
4142
#include <openssl/ssl.h>
4243
#include <openssl/ec.h>
4344
#include <openssl/ecdh.h>
@@ -59,10 +60,51 @@
5960
# define NODE__HAVE_TLSEXT_STATUS_CB
6061
#endif // !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_set_tlsext_status_cb)
6162

63+
#define DEFAULT_CIPHER_LIST_V10_38 "ECDHE-RSA-AES128-SHA256:" \
64+
"AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH"
65+
66+
#define DEFAULT_CIPHER_LIST_V10_39 "ECDHE-RSA-AES128-SHA256:" \
67+
"AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH"
68+
69+
#define DEFAULT_CIPHER_LIST_V12_2 "ECDHE-RSA-AES128-SHA256:" \
70+
"DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:" \
71+
"HIGH:!MD5:!aNULL"
72+
73+
#define DEFAULT_CIPHER_LIST_V12_3 "ECDHE-RSA-AES128-SHA256:" \
74+
"DHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:"\
75+
"!RC4:!MD5:!aNULL"
76+
77+
#define DEFAULT_CIPHER_LIST_HEAD "ECDHE-RSA-AES256-SHA384:" \
78+
"DHE-RSA-AES256-SHA384:" \
79+
"ECDHE-RSA-AES256-SHA256:" \
80+
"DHE-RSA-AES256-SHA256:" \
81+
"ECDHE-RSA-AES128-SHA256:" \
82+
"DHE-RSA-AES128-SHA256:" \
83+
"HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:" \
84+
"!PSK:!SRP:!CAMELLIA"
85+
86+
static inline const char * legacy_cipher_list(const char * ver) {
87+
if (ver == NULL) {
88+
return NULL;
89+
}
90+
if (strncmp(ver, "v0.10.38", 8) == 0) {
91+
return DEFAULT_CIPHER_LIST_V10_38;
92+
} else if (strncmp(ver, "v0.10.39", 8) == 0) {
93+
return DEFAULT_CIPHER_LIST_V10_39;
94+
} else if (strncmp(ver, "v0.12.2", 7) == 0) {
95+
return DEFAULT_CIPHER_LIST_V12_2;
96+
} else if (strncmp(ver, "v0.12.3", 7) == 0) {
97+
return DEFAULT_CIPHER_LIST_V12_3;
98+
} else {
99+
return NULL;
100+
}
101+
}
102+
62103
namespace node {
63104

64105
extern bool SSL2_ENABLE;
65106
extern bool SSL3_ENABLE;
107+
extern const char * DEFAULT_CIPHER_LIST;
66108

67109
namespace crypto {
68110

0 commit comments

Comments
 (0)