admin管理员组

文章数量:1287489

Node.js sends the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher by default to protect itself against the POODLE attack.

I'm trying to avoid sending this cipher (even though this may pose a security risk) by overriding the TLS ciphers with a custom list of ciphers.

However, Node.js keeps sending the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher no matter what I do. I'm trying to deliberately avoid sending this cipher to mimic the TLS negotiation of Firefox/Chrome.

Here's the code I use to modify and check which ciphers Node is sending:

var request = require('request');

var ciphers = [
    'ECDHE-ECDSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-ECDSA-AES256-SHA',
    'ECDHE-ECDSA-AES128-SHA',
    'ECDHE-RSA-AES128-SHA',
    'ECDHE-RSA-AES256-SHA',
    'DHE-RSA-AES128-SHA',
    'DHE-RSA-AES256-SHA',
    'AES128-SHA',
    'AES256-SHA',
    'DES-CBC3-SHA'
].join(':');

var options = {
    ciphers: ciphers,
    secureProtocol: 'TLSv1_2_method',
    url: ''
};

request(options, function (error, response, body){
    if (!error) {
        console.log(body);
    }
    else {
        console.log(error);
    }
});

Is there any way to disable sending this cipher in Node.js?

Node.js sends the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher by default to protect itself against the POODLE attack.

I'm trying to avoid sending this cipher (even though this may pose a security risk) by overriding the TLS ciphers with a custom list of ciphers.

However, Node.js keeps sending the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher no matter what I do. I'm trying to deliberately avoid sending this cipher to mimic the TLS negotiation of Firefox/Chrome.

Here's the code I use to modify and check which ciphers Node is sending:

var request = require('request');

var ciphers = [
    'ECDHE-ECDSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-ECDSA-AES256-SHA',
    'ECDHE-ECDSA-AES128-SHA',
    'ECDHE-RSA-AES128-SHA',
    'ECDHE-RSA-AES256-SHA',
    'DHE-RSA-AES128-SHA',
    'DHE-RSA-AES256-SHA',
    'AES128-SHA',
    'AES256-SHA',
    'DES-CBC3-SHA'
].join(':');

var options = {
    ciphers: ciphers,
    secureProtocol: 'TLSv1_2_method',
    url: 'https://www.howsmyssl./a/check'
};

request(options, function (error, response, body){
    if (!error) {
        console.log(body);
    }
    else {
        console.log(error);
    }
});

Is there any way to disable sending this cipher in Node.js?

Share Improve this question edited Feb 10, 2016 at 9:20 Elad Nava asked Feb 7, 2016 at 14:46 Elad NavaElad Nava 7,8963 gold badges44 silver badges63 bronze badges 3
  • I think this has nothing to do directly with node.js but with the request module that you are using. Or did you debug the problem into the node.js core? – crackmigg Commented Feb 7, 2016 at 17:43
  • I'm not sure this is correct: "... sending this cipher is not necessary if I deliberately disable protocol downgrade (by forcing TLS 1.2". I believe all versions of TLS suffer the downgrade attacks because TLS does not use a {min-TLS,max-TLS} version pair. Instead, TLS_FALLBACK_SCS works over time, so it would apply with consecutive connections, and not a single connection attempt in a vacuum. – jww Commented Feb 7, 2016 at 19:41
  • @migg it is in the Node.js core -- check out ssl_lib.c#L1472 which appears to be the place in Node.js where the SCSV cipher is added to the Client Hello (I may be wrong about the line but it's in that file). The request npm module depends on Node.js' tls package which handles the TLS connection via openssl. @jww you may be right that it is insecure, however, I'd still like to disable it for a test case, and don't care about the security implications at this time. – Elad Nava Commented Feb 7, 2016 at 21:08
Add a ment  | 

2 Answers 2

Reset to default 4 +50

Given that this issue is related to Node.js, there is one simple method to deal with your issue without actually digging to much into it:

Put a web proxy in front of your Node.js process and let it handle the plete SSL connection. In the Node.js code itself, you would only send a request to a local HTTP server.

Example configuration with nginx (inside the http directive):

server {
   listen 8080;
   location / {
      resolver 8.8.8.8;
      proxy_pass https://www.howsmyssl.$uri$is_args&args;
      proxy_ssl_protocols TLSv1.2;
      proxy_ssl_ciphers AESGCM:!aNULL;
   }
}

And change the nodejs to:

var request = require('request');

var options = {
    url: 'http://localhost:8080/a/check'
};

request(options, function (error, response, body){
    if (!error) {
        console.log(body);
    }
    else {
        console.log(error);
    }
});

Unfortunately though I actually did this, and the result was the same:

{"given_cipher_suites":["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","TLS_DH_DSS_WITH_AES_256_GCM_SHA384","TLS_DHE_DSS_WITH_AES_256_GCM_SHA384","TLS_DH_RSA_WITH_AES_256_GCM_SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384","TLS_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_DH_DSS_WITH_AES_128_GCM_SHA256","TLS_DHE_DSS_WITH_AES_128_GCM_SHA256","TLS_DH_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_EMPTY_RENEGOTIATION_INFO_SCSV"],"ephemeral_keys_supported":true,"session_ticket_supported":true,"tls_pression_supported":false,"unknown_cipher_suite_supported":false,"beast_vuln":false,"able_to_detect_n_minus_one_splitting":false,"insecure_cipher_suites":{},"tls_version":"TLS 1.2","rating":"Probably Okay"}

This basically means that its probably standard OpenSSL behavior.

There are options which can be set in OpenSSL using SSL_CTX_set_options

Especially interesting here is the SECURE RENEGOTIATION section and this option:

SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION

Allow legacy insecure renegotiation between OpenSSL and unpatched clients or servers. See the SECURE RENEGOTIATION section for more details.

I am not certain though if this actually prevents the renegotiation cipher from being send. If this option is actually correct, there might be a way to either patch Node.js to use that option or repile OpenSSL with that option.

There would of course also be the option to use an old unpatched version. From my understanding though TLS_EMPTY_RENEGOTIATION_INFO_SCSV is not related to POODLE, but from an older fix:

CVE-2009-3555 (OpenSSL advisory) 5th November 2009:
Implement RFC5746 to address vulnerabilities in SSL/TLS renegotiation. Fixed in OpenSSL 0.9.8m (Affected 0.9.8l, 0.9.8k, 0.9.8j, 0.9.8i, 0.9.8h, 0.9.8g, 0.9.8f, 0.9.8e, 0.9.8d, 0.9.8c, 0.9.8b, 0.9.8a, 0.9.8)

Modern Node.js es with a statically linked OpenSSL however and does not support OpenSSL 0.9.8, so you would need an older version of Node.js regardless... or use the nginx stuff with an unpatched OpenSSL...

This is kind of where I am stuck. Its not really an plete answer, but I think its at least worth to share.

To sum it up though, I think if you want to do this without repiling use nginx with and unpatched OpenSSL, and configure multiple servers, one for each client you want to mimic.

If you require this to be soley done in node, your best bet is to patch OpenSSL there directly and repile node.

It seems that TLS_EMPTY_RENEGOTIATION_INFO is a placeholder cipher suite that performs the same function as the Extension "renegotiation_info". Furthermore, it seems that OpenSSL does not have a way to set the "renegotiation_info" extension and that this empty Cipher Suite is required.

Source: c-openssl-setting-list-of-ciphers
Source: RFC 5746 Section-3.3

本文标签: javascriptAvoid sending TLSEMPTYRENEGOTIATIONINFOSCSV cipher in TLS Client HelloStack Overflow