Avoid sending TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher in TLS Client Hello
Asked Answered
D

2

15

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.com/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?

Deweydewhirst answered 7/2, 2016 at 14:46 Comment(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?Dawn
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.Interjoin
@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. @Interjoin 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.Deweydewhirst
M
4

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 complete 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.com$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_compression_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 recompile 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 comes 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 complete answer, but I think its at least worth to share.

To sum it up though, I think if you want to do this without recompiling 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 recompile node.

Monosymmetric answered 16/2, 2016 at 0:11 Comment(2)
This is the way to go. I always let Ngnix handle my SSL-- far less risk of problems in the future.Havard
Thanks so much for your detailed answer! I think I will end up patching Node.js and compiling it manually, since I wasn't able to avoid sending this cipher even on node 0.8.x.Deweydewhirst
M
0

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

Medicaid answered 26/5, 2021 at 21:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.