mirror of
https://github.com/nodejs/node-v0.x-archive.git
synced 2026-04-28 03:01:10 -04:00
Compare commits
3 Commits
v0.12.4
...
fix-api-do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c681f8c994 | ||
|
|
02a549ed2b | ||
|
|
67d9a56251 |
@@ -109,6 +109,60 @@ handshake extensions allowing you:
|
||||
* SNI - to use one TLS server for multiple hostnames with different SSL
|
||||
certificates.
|
||||
|
||||
## Modifying the Default Cipher Suite
|
||||
|
||||
Node.js is built with a default suite of enabled and disabled ciphers.
|
||||
Currently, the default cipher suite is:
|
||||
|
||||
ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH
|
||||
|
||||
This default can be overridden entirely using the `--cipher-list` command line
|
||||
switch or `NODE_CIPHER_LIST` environment variable. For instance:
|
||||
|
||||
node --cipher-list=ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384
|
||||
|
||||
Setting the environment variable would have the same effect:
|
||||
|
||||
NODE_CIPHER_LIST=ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384
|
||||
|
||||
CAUTION: The default cipher suite 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
|
||||
`--cipher-list` and `NODE_CIPHER_LIST` options should only be used if
|
||||
absolutely necessary.
|
||||
|
||||
### Using Legacy Default Cipher Suite ###
|
||||
|
||||
It is possible for the built-in default cipher suite to change from one release
|
||||
of Node.js to another. For instance, v0.10.39 uses a different default than
|
||||
v0.10.38. Such changes can cause issues with applications written to assume
|
||||
certain specific defaults. To help buffer applications against such changes,
|
||||
the `--enable-legacy-cipher-list` command line switch or `NODE_LEGACY_CIPHER_LIST`
|
||||
environment variable can be set to specify a specific preset default:
|
||||
|
||||
# Use the v0.10.38 defaults
|
||||
node --enable-legacy-cipher-list=v0.10.38
|
||||
// or
|
||||
NODE_LEGACY_CIPHER_LIST=v0.10.38
|
||||
|
||||
Currently, the values supported for the `enable-legacy-cipher-list` switch and
|
||||
`NODE_LEGACY_CIPHER_LIST` environment variable include:
|
||||
|
||||
v0.10.38 - To enable the default cipher suite used in v0.10.38
|
||||
|
||||
ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH
|
||||
|
||||
These legacy cipher suites are also made available for use via the
|
||||
`getLegacyCiphers()` method:
|
||||
|
||||
var tls = require('tls');
|
||||
console.log(tls.getLegacyCiphers('v0.10.38'));
|
||||
|
||||
CAUTION: Changes to the default cipher suite are typically made in order to
|
||||
strengthen the default security for applications running within Node.js.
|
||||
Reverting back to the defaults used by older releases can weaken the security
|
||||
of your applications. The legacy cipher suites should only be used if absolutely
|
||||
necessary.
|
||||
|
||||
## tls.getCiphers()
|
||||
|
||||
@@ -151,13 +205,13 @@ automatically set as a listener for the [secureConnection][] event. The
|
||||
conjunction with the `honorCipherOrder` option described below to
|
||||
prioritize the non-CBC cipher.
|
||||
|
||||
Defaults to `AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH`.
|
||||
Defaults to `ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH`.
|
||||
Consult the [OpenSSL cipher list format documentation] for details on the
|
||||
format. ECDH (Elliptic Curve Diffie-Hellman) ciphers are not yet supported.
|
||||
|
||||
|
||||
`AES128-GCM-SHA256` is used when node.js is linked against OpenSSL 1.0.1
|
||||
or newer and the client speaks TLS 1.2, RC4 is used as a secure fallback.
|
||||
or newer and the client speaks TLS 1.2.
|
||||
|
||||
**NOTE**: Previous revisions of this section suggested `AES256-SHA` as an
|
||||
acceptable cipher. Unfortunately, `AES256-SHA` is a CBC cipher and therefore
|
||||
@@ -333,7 +387,7 @@ Here is an example of a client of echo server as described previously:
|
||||
// These are necessary only if using the client certificate authentication
|
||||
key: fs.readFileSync('client-key.pem'),
|
||||
cert: fs.readFileSync('client-cert.pem'),
|
||||
|
||||
|
||||
// This is necessary only if the server uses the self-signed certificate
|
||||
ca: [ fs.readFileSync('server-cert.pem') ]
|
||||
};
|
||||
@@ -525,7 +579,7 @@ A ClearTextStream is the `clear` member of a SecurePair object.
|
||||
|
||||
### Event: 'secureConnect'
|
||||
|
||||
This event is emitted after a new connection has been successfully handshaked.
|
||||
This event is emitted after a new connection has been successfully handshaked.
|
||||
The listener will be called no matter if the server's certificate was
|
||||
authorized or not. It is up to the user to test `cleartextStream.authorized`
|
||||
to see if the server certificate was signed by one of the specified CAs.
|
||||
@@ -550,14 +604,14 @@ some properties corresponding to the field of the certificate.
|
||||
|
||||
Example:
|
||||
|
||||
{ subject:
|
||||
{ subject:
|
||||
{ C: 'UK',
|
||||
ST: 'Acknack Ltd',
|
||||
L: 'Rhys Jones',
|
||||
O: 'node.js',
|
||||
OU: 'Test TLS Certificate',
|
||||
CN: 'localhost' },
|
||||
issuer:
|
||||
issuer:
|
||||
{ C: 'UK',
|
||||
ST: 'Acknack Ltd',
|
||||
L: 'Rhys Jones',
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<li><a href="http://nodejs.org/documentation/">About Docs</a></li>
|
||||
<li><a href="http://nodejs.org/documentation/tutorials/">Tutorials</a></li>
|
||||
<li><a href="http://nodejs.org/documentation/contributing/">Contributing</a></li>
|
||||
<li><a href="http://nodejs.org/documentation/workflow/">Workflow</a></li>
|
||||
<li><a href="http://nodejs.org/documentation/localization/">Localization</a></li>
|
||||
<li class="active"><a href="http://nodejs.org/api/">API Docs</a></li>
|
||||
</ul>
|
||||
|
||||
14
lib/tls.js
14
lib/tls.js
@@ -19,6 +19,8 @@
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var _crypto = process.binding('crypto');
|
||||
|
||||
var crypto = require('crypto');
|
||||
var util = require('util');
|
||||
var net = require('net');
|
||||
@@ -31,8 +33,9 @@ var constants = require('constants');
|
||||
|
||||
var Timer = process.binding('timer_wrap').Timer;
|
||||
|
||||
var DEFAULT_CIPHERS = 'ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:' + // TLS 1.2
|
||||
'RC4:HIGH:!MD5:!aNULL:!EDH'; // TLS 1.0
|
||||
var DEFAULT_CIPHERS = _crypto.DEFAULT_CIPHER_LIST;
|
||||
|
||||
exports.getLegacyCiphers = _crypto.getLegacyCiphers;
|
||||
|
||||
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
|
||||
// every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more
|
||||
@@ -44,7 +47,7 @@ exports.CLIENT_RENEG_WINDOW = 600;
|
||||
exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
exports.getCiphers = function() {
|
||||
var names = process.binding('crypto').getSSLCiphers();
|
||||
var names = _crypto.getSSLCiphers();
|
||||
// Drop all-caps names in favor of their lowercase aliases,
|
||||
var ctx = {};
|
||||
names.forEach(function(name) {
|
||||
@@ -65,7 +68,7 @@ if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
|
||||
|
||||
var Connection = null;
|
||||
try {
|
||||
Connection = process.binding('crypto').Connection;
|
||||
Connection = _crypto.Connection;
|
||||
} catch (e) {
|
||||
throw new Error('node.js not compiled with openssl crypto support.');
|
||||
}
|
||||
@@ -1335,6 +1338,9 @@ exports.connect = function(/* [port, host], options, cb */) {
|
||||
var defaults = {
|
||||
rejectUnauthorized: '0' !== process.env.NODE_TLS_REJECT_UNAUTHORIZED
|
||||
};
|
||||
if (DEFAULT_CIPHERS != _crypto.getLegacyCiphers('v0.10.38')) {
|
||||
defaults.ciphers = DEFAULT_CIPHERS;
|
||||
}
|
||||
options = util._extend(defaults, options || {});
|
||||
|
||||
options.secureOptions = crypto._getSecureOptions(options.secureProtocol,
|
||||
|
||||
39
src/node.cc
39
src/node.cc
@@ -2566,6 +2566,8 @@ static void PrintHelp() {
|
||||
" --max-stack-size=val set max v8 stack size (bytes)\n"
|
||||
" --enable-ssl2 enable ssl2\n"
|
||||
" --enable-ssl3 enable ssl3\n"
|
||||
" --cipher-list=val specify the default TLS cipher list\n"
|
||||
" --enable-legacy-cipher-list=v0.10.38 \n"
|
||||
"\n"
|
||||
"Environment variables:\n"
|
||||
#ifdef _WIN32
|
||||
@@ -2577,6 +2579,8 @@ static void PrintHelp() {
|
||||
"NODE_MODULE_CONTEXTS Set to 1 to load modules in their own\n"
|
||||
" global contexts.\n"
|
||||
"NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL\n"
|
||||
"NODE_CIPHER_LIST Override the default TLS cipher list\n"
|
||||
"NODE_LEGACY_CIPHER_LIST=v0.10.38\n"
|
||||
"\n"
|
||||
"Documentation can be found at http://nodejs.org/\n");
|
||||
}
|
||||
@@ -2584,6 +2588,7 @@ static void PrintHelp() {
|
||||
// Parse node command line arguments.
|
||||
static void ParseArgs(int argc, char **argv) {
|
||||
int i;
|
||||
bool using_legacy_cipher_list = false;
|
||||
|
||||
// TODO use parse opts
|
||||
for (i = 1; i < argc; i++) {
|
||||
@@ -2652,6 +2657,21 @@ static void ParseArgs(int argc, char **argv) {
|
||||
} else if (strcmp(arg, "--throw-deprecation") == 0) {
|
||||
argv[i] = const_cast<char*>("");
|
||||
throw_deprecation = true;
|
||||
} else if (strncmp(arg, "--cipher-list=", 14) == 0) {
|
||||
if (!using_legacy_cipher_list) {
|
||||
DEFAULT_CIPHER_LIST = arg + 14;
|
||||
}
|
||||
argv[i] = const_cast<char*>("");
|
||||
} else if (strncmp(arg, "--enable-legacy-cipher-list=", 28) == 0) {
|
||||
const char * legacy_list = legacy_cipher_list(arg+28);
|
||||
if (legacy_list != NULL) {
|
||||
using_legacy_cipher_list = true;
|
||||
DEFAULT_CIPHER_LIST = legacy_list;
|
||||
} else {
|
||||
fprintf(stderr, "Error: An unknown legacy cipher list was specified\n");
|
||||
exit(9);
|
||||
}
|
||||
argv[i] = const_cast<char*>("");
|
||||
} else if (argv[i][0] != '-') {
|
||||
break;
|
||||
}
|
||||
@@ -2946,6 +2966,25 @@ char** Init(int argc, char *argv[]) {
|
||||
v8argv[option_end_index + 1] = const_cast<char*>("v8debug");
|
||||
}
|
||||
|
||||
const char * cipher_list = getenv("NODE_CIPHER_LIST");
|
||||
if (cipher_list != NULL) {
|
||||
DEFAULT_CIPHER_LIST = cipher_list;
|
||||
}
|
||||
// Allow the NODE_LEGACY_CIPHER_LIST envar to override the other
|
||||
// cipher list options. NODE_LEGACY_CIPHER_LIST=v0.10.38 will use
|
||||
// the cipher list from v0.10.38
|
||||
const char * leg_cipher_id = getenv("NODE_LEGACY_CIPHER_LIST");
|
||||
if (leg_cipher_id != NULL) {
|
||||
const char * leg_cipher_list =
|
||||
legacy_cipher_list(leg_cipher_id);
|
||||
if (leg_cipher_list != NULL) {
|
||||
DEFAULT_CIPHER_LIST = leg_cipher_list;
|
||||
} else {
|
||||
fprintf(stderr, "Error: An unknown legacy cipher list was specified\n");
|
||||
exit(9);
|
||||
}
|
||||
}
|
||||
|
||||
// For the normal stack which moves from high to low addresses when frames
|
||||
// are pushed, we can compute the limit as stack_size bytes below the
|
||||
// the address of a stack variable (e.g. &stack_var) as an approximation
|
||||
|
||||
@@ -71,6 +71,7 @@ const char* root_certs[] = {
|
||||
|
||||
bool SSL2_ENABLE = false;
|
||||
bool SSL3_ENABLE = false;
|
||||
const char * DEFAULT_CIPHER_LIST = DEFAULT_CIPHER_LIST_HEAD;
|
||||
|
||||
namespace crypto {
|
||||
|
||||
@@ -802,7 +803,7 @@ size_t ClientHelloParser::Write(const uint8_t* data, size_t len) {
|
||||
HandleScope scope;
|
||||
|
||||
assert(state_ != kEnded);
|
||||
|
||||
|
||||
// Just accumulate data, everything will be pushed to BIO later
|
||||
if (state_ == kPaused) return 0;
|
||||
|
||||
@@ -4190,6 +4191,21 @@ static void array_push_back(const TypeName* md,
|
||||
arr->Set(arr->Length(), String::New(from));
|
||||
}
|
||||
|
||||
// borrowed from v8
|
||||
// (see http://v8.googlecode.com/svn/trunk/samples/shell.cc)
|
||||
const char* ToCString(const node::Utf8Value& value) {
|
||||
return *value ? *value : "<string conversion failed>";
|
||||
}
|
||||
|
||||
Handle<Value> DefaultCiphers(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
node::Utf8Value key(args[0]);
|
||||
const char * list = legacy_cipher_list(ToCString(key));
|
||||
if (list == NULL) {
|
||||
list = DEFAULT_CIPHER_LIST_HEAD;
|
||||
}
|
||||
return scope.Close(v8::String::New(list));
|
||||
}
|
||||
|
||||
Handle<Value> GetCiphers(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
@@ -4264,6 +4280,13 @@ void InitCrypto(Handle<Object> target) {
|
||||
|
||||
NODE_DEFINE_CONSTANT(target, SSL3_ENABLE);
|
||||
NODE_DEFINE_CONSTANT(target, SSL2_ENABLE);
|
||||
|
||||
(target)->ForceSet(
|
||||
v8::String::New("DEFAULT_CIPHER_LIST"),
|
||||
v8::String::New(DEFAULT_CIPHER_LIST),
|
||||
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete));
|
||||
|
||||
NODE_SET_METHOD(target, "getLegacyCiphers", DefaultCiphers);
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "node_object_wrap.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
@@ -43,10 +44,29 @@
|
||||
|
||||
#define EVP_F_EVP_DECRYPTFINAL 101
|
||||
|
||||
#define DEFAULT_CIPHER_LIST_V10_38 "ECDHE-RSA-AES128-SHA256:" \
|
||||
"AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH"
|
||||
|
||||
#define DEFAULT_CIPHER_LIST_HEAD "ECDHE-RSA-AES128-SHA256:" \
|
||||
"AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH"
|
||||
|
||||
static inline const char * legacy_cipher_list(const char * ver) {
|
||||
if (ver == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (strncmp(ver, "v0.10.38", 8) == 0) {
|
||||
return DEFAULT_CIPHER_LIST_V10_38;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace node {
|
||||
|
||||
extern bool SSL2_ENABLE;
|
||||
extern bool SSL3_ENABLE;
|
||||
extern const char * DEFAULT_CIPHER_LIST;
|
||||
|
||||
namespace crypto {
|
||||
|
||||
|
||||
70
test/simple/test-tls-cipher-list.js
Normal file
70
test/simple/test-tls-cipher-list.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var assert = require('assert');
|
||||
var tls = require('tls');
|
||||
var crypto = process.binding('crypto');
|
||||
|
||||
function doTest(checklist, env, useswitch) {
|
||||
var options;
|
||||
if (env && useswitch === 1) {
|
||||
options = {env:env};
|
||||
}
|
||||
var args = ['-e', 'console.log(process.binding(\'crypto\').DEFAULT_CIPHER_LIST)'];
|
||||
|
||||
switch(useswitch) {
|
||||
case 1:
|
||||
// Test --cipher-test
|
||||
args.unshift('--cipher-list=' + env);
|
||||
break;
|
||||
case 2:
|
||||
// Test --enable-legacy-cipher-list
|
||||
args.unshift('--enable-legacy-cipher-list=' + env);
|
||||
break;
|
||||
case 3:
|
||||
// Test NODE_LEGACY_CIPHER_LIST
|
||||
if (env) options = {env:{"NODE_LEGACY_CIPHER_LIST": env}};
|
||||
break;
|
||||
default:
|
||||
// Test NODE_CIPHER_LIST
|
||||
if (env) options = {env:env};
|
||||
}
|
||||
|
||||
var out = '';
|
||||
spawn(process.execPath, args, options).
|
||||
stdout.
|
||||
on('data', function(data) {
|
||||
out += data;
|
||||
}).
|
||||
on('end', function() {
|
||||
assert.equal(out.trim(), checklist);
|
||||
});
|
||||
}
|
||||
|
||||
doTest(crypto.DEFAULT_CIPHER_LIST); // test the default
|
||||
doTest('ABC', {'NODE_CIPHER_LIST':'ABC'}); // test the envar
|
||||
doTest('ABC', 'ABC', 1); // test the --cipher-list switch
|
||||
|
||||
['v0.10.38'].forEach(function(ver) {
|
||||
doTest(tls.getLegacyCiphers(ver), ver, 2);
|
||||
doTest(tls.getLegacyCiphers(ver), ver, 3);
|
||||
});
|
||||
@@ -49,7 +49,7 @@ server.listen(common.PORT, '127.0.0.1', function() {
|
||||
rejectUnauthorized: false
|
||||
}, function() {
|
||||
var cipher = client.getCipher();
|
||||
assert.equal(cipher.name, cipher_list[0]);
|
||||
assert.equal(cipher.name, cipher_list[1]);
|
||||
assert(cipher_version_pattern.test(cipher.version));
|
||||
client.end();
|
||||
server.close();
|
||||
|
||||
Reference in New Issue
Block a user