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 <ben@strongloop.com> PR-URL: https://github.com/nodejs/node/pull/2412
This commit is contained in:
parent
3a7be23f98
commit
5ba868f024
@ -77,6 +77,44 @@ handshake extensions allowing you:
|
||||
* SNI - to use one TLS server for multiple hostnames with different SSL
|
||||
certificates.
|
||||
|
||||
## Modifying the Default TLS Cipher suite
|
||||
|
||||
Node.js is built with a default suite of enabled and disabled TLS ciphers.
|
||||
Currently, the default cipher suite is:
|
||||
|
||||
ECDHE-RSA-AES128-GCM-SHA256:
|
||||
ECDHE-ECDSA-AES128-GCM-SHA256:
|
||||
ECDHE-RSA-AES256-GCM-SHA384:
|
||||
ECDHE-ECDSA-AES256-GCM-SHA384:
|
||||
DHE-RSA-AES128-GCM-SHA256:
|
||||
ECDHE-RSA-AES128-SHA256:
|
||||
DHE-RSA-AES128-SHA256:
|
||||
ECDHE-RSA-AES256-SHA384:
|
||||
DHE-RSA-AES256-SHA384:
|
||||
ECDHE-RSA-AES256-SHA256:
|
||||
DHE-RSA-AES256-SHA256:
|
||||
HIGH:
|
||||
!aNULL:
|
||||
!eNULL:
|
||||
!EXPORT:
|
||||
!DES:
|
||||
!RC4:
|
||||
!MD5:
|
||||
!PSK:
|
||||
!SRP:
|
||||
!CAMELLIA
|
||||
|
||||
This default can be overriden entirely using the `--tls-cipher-list` command
|
||||
line switch. For instance, the following makes
|
||||
`ECDHE-RSA-AES128-GCM-SHA256:!RC4` the default TLS cipher suite:
|
||||
|
||||
node --tls-cipher-list="ECDHE-RSA-AES128-GCM-SHA256:!RC4"
|
||||
|
||||
Note that the default cipher suite included within Node.js has been carefully
|
||||
selected to reflect current security best practices and risk mitigation.
|
||||
Changing the default cipher suite can have a significant impact on the security
|
||||
of an application. The `--tls-cipher-list` switch should by used only if
|
||||
absolutely necessary.
|
||||
|
||||
## Perfect Forward Secrecy
|
||||
|
||||
@ -138,7 +176,7 @@ automatically set as a listener for the [secureConnection][] event. The
|
||||
- `crl` : Either a string or list of strings of PEM encoded CRLs (Certificate
|
||||
Revocation List)
|
||||
|
||||
- `ciphers`: A string describing the ciphers to use or exclude, seperated by
|
||||
- `ciphers`: A string describing the ciphers to use or exclude, separated by
|
||||
`:`. The default cipher suite is:
|
||||
|
||||
ECDHE-RSA-AES128-GCM-SHA256:
|
||||
|
@ -64,6 +64,9 @@ and servers.
|
||||
|
||||
--v8-options print v8 command line options
|
||||
|
||||
--tls-cipher-list=list use an alternative default TLS cipher list
|
||||
(available only when Node.js is built with
|
||||
OpenSSL and crypto support enabled)
|
||||
|
||||
.SH ENVIRONMENT VARIABLES
|
||||
|
||||
|
25
lib/tls.js
25
lib/tls.js
@ -5,6 +5,7 @@ const url = require('url');
|
||||
const util = require('util');
|
||||
const binding = process.binding('crypto');
|
||||
const Buffer = require('buffer').Buffer;
|
||||
const constants = require('constants');
|
||||
|
||||
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
|
||||
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
|
||||
@ -15,29 +16,7 @@ exports.CLIENT_RENEG_WINDOW = 600;
|
||||
|
||||
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
exports.DEFAULT_CIPHERS = [
|
||||
'ECDHE-RSA-AES128-GCM-SHA256',
|
||||
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
||||
'ECDHE-RSA-AES256-GCM-SHA384',
|
||||
'ECDHE-ECDSA-AES256-GCM-SHA384',
|
||||
'DHE-RSA-AES128-GCM-SHA256',
|
||||
'ECDHE-RSA-AES128-SHA256',
|
||||
'DHE-RSA-AES128-SHA256',
|
||||
'ECDHE-RSA-AES256-SHA384',
|
||||
'DHE-RSA-AES256-SHA384',
|
||||
'ECDHE-RSA-AES256-SHA256',
|
||||
'DHE-RSA-AES256-SHA256',
|
||||
'HIGH',
|
||||
'!aNULL',
|
||||
'!eNULL',
|
||||
'!EXPORT',
|
||||
'!DES',
|
||||
'!RC4',
|
||||
'!MD5',
|
||||
'!PSK',
|
||||
'!SRP',
|
||||
'!CAMELLIA'
|
||||
].join(':');
|
||||
exports.DEFAULT_CIPHERS = constants.defaultCipherList;
|
||||
|
||||
exports.DEFAULT_ECDH_CURVE = 'prime256v1';
|
||||
|
||||
|
@ -3111,6 +3111,9 @@ static void PrintHelp() {
|
||||
" --track-heap-objects track heap object allocations for heap "
|
||||
"snapshots\n"
|
||||
" --v8-options print v8 command line options\n"
|
||||
#if HAVE_OPENSSL
|
||||
" --tls-cipher-list=val use an alternative default TLS cipher list\n"
|
||||
#endif
|
||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||
" --icu-data-dir=dir set ICU data load path to dir\n"
|
||||
" (overrides NODE_ICU_DATA)\n"
|
||||
@ -3242,6 +3245,10 @@ static void ParseArgs(int* argc,
|
||||
} else if (strcmp(arg, "--v8-options") == 0) {
|
||||
new_v8_argv[new_v8_argc] = "--help";
|
||||
new_v8_argc += 1;
|
||||
#if HAVE_OPENSSL
|
||||
} else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) {
|
||||
default_cipher_list = arg + 18;
|
||||
#endif
|
||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
|
||||
icu_data_dir = arg + 15;
|
||||
|
@ -24,6 +24,10 @@ namespace node {
|
||||
using v8::Handle;
|
||||
using v8::Object;
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
const char* default_cipher_list = DEFAULT_CIPHER_LIST_CORE;
|
||||
#endif
|
||||
|
||||
void DefineErrnoConstants(Handle<Object> target) {
|
||||
#ifdef E2BIG
|
||||
NODE_DEFINE_CONSTANT(target, E2BIG);
|
||||
@ -1108,6 +1112,17 @@ void DefineUVConstants(Handle<Object> target) {
|
||||
NODE_DEFINE_CONSTANT(target, UV_UDP_REUSEADDR);
|
||||
}
|
||||
|
||||
void DefineCryptoConstants(Handle<Object> target) {
|
||||
#if HAVE_OPENSSL
|
||||
NODE_DEFINE_STRING_CONSTANT(target,
|
||||
"defaultCoreCipherList",
|
||||
DEFAULT_CIPHER_LIST_CORE);
|
||||
NODE_DEFINE_STRING_CONSTANT(target,
|
||||
"defaultCipherList",
|
||||
default_cipher_list);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DefineConstants(Handle<Object> target) {
|
||||
DefineErrnoConstants(target);
|
||||
DefineWindowsErrorConstants(target);
|
||||
@ -1115,6 +1130,7 @@ void DefineConstants(Handle<Object> target) {
|
||||
DefineOpenSSLConstants(target);
|
||||
DefineSystemConstants(target);
|
||||
DefineUVConstants(target);
|
||||
DefineCryptoConstants(target);
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
@ -4,7 +4,36 @@
|
||||
#include "node.h"
|
||||
#include "v8.h"
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
#define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256:" \
|
||||
"ECDHE-RSA-AES256-GCM-SHA384:" \
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384:" \
|
||||
"DHE-RSA-AES128-GCM-SHA256:" \
|
||||
"ECDHE-RSA-AES128-SHA256:" \
|
||||
"DHE-RSA-AES128-SHA256:" \
|
||||
"ECDHE-RSA-AES256-SHA384:" \
|
||||
"DHE-RSA-AES256-SHA384:" \
|
||||
"ECDHE-RSA-AES256-SHA256:" \
|
||||
"DHE-RSA-AES256-SHA256:" \
|
||||
"HIGH:" \
|
||||
"!aNULL:" \
|
||||
"!eNULL:" \
|
||||
"!EXPORT:" \
|
||||
"!DES:" \
|
||||
"!RC4:" \
|
||||
"!MD5:" \
|
||||
"!PSK:" \
|
||||
"!SRP:" \
|
||||
"!CAMELLIA"
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
extern const char* default_cipher_list;
|
||||
#endif
|
||||
|
||||
void DefineConstants(v8::Handle<v8::Object> target);
|
||||
} // namespace node
|
||||
|
||||
|
@ -12,6 +12,22 @@
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
// Variation on NODE_DEFINE_CONSTANT that sets a String value.
|
||||
#define NODE_DEFINE_STRING_CONSTANT(target, name, constant) \
|
||||
do { \
|
||||
v8::Isolate* isolate = target->GetIsolate(); \
|
||||
v8::Local<v8::String> constant_name = \
|
||||
v8::String::NewFromUtf8(isolate, name); \
|
||||
v8::Local<v8::String> constant_value = \
|
||||
v8::String::NewFromUtf8(isolate, constant); \
|
||||
v8::PropertyAttribute constant_attributes = \
|
||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete); \
|
||||
target->ForceSet(isolate->GetCurrentContext(), \
|
||||
constant_name, \
|
||||
constant_value, \
|
||||
constant_attributes); \
|
||||
} while (0)
|
||||
|
||||
namespace node {
|
||||
|
||||
// Forward declaration
|
||||
|
32
test/parallel/test-tls-cipher-list.js
Normal file
32
test/parallel/test-tls-cipher-list.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
if (!common.hasCrypto) {
|
||||
console.log('1..0 # Skipped: missing crypto');
|
||||
return;
|
||||
}
|
||||
|
||||
const assert = require('assert');
|
||||
const spawn = require('child_process').spawn;
|
||||
const defaultCoreList = require('constants').defaultCoreCipherList;
|
||||
|
||||
function doCheck(arg, check) {
|
||||
var out = '';
|
||||
var arg = arg.concat([
|
||||
'-pe',
|
||||
'require("constants").defaultCipherList'
|
||||
]);
|
||||
spawn(process.execPath, arg, {}).
|
||||
on('error', assert.fail).
|
||||
stdout.on('data', function(chunk) {
|
||||
out += chunk;
|
||||
}).on('end', function() {
|
||||
assert.equal(out.trim(), check);
|
||||
}).on('error', assert.fail);
|
||||
}
|
||||
|
||||
// test the default unmodified version
|
||||
doCheck([], defaultCoreList);
|
||||
|
||||
// test the command line switch by itself
|
||||
doCheck(['--tls-cipher-list=ABC'], 'ABC');
|
Loading…
x
Reference in New Issue
Block a user