【问题标题】:Avoid sending TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher in TLS Client Hello避免在 TLS 客户端 Hello 中发送 TLS_EMPTY_RENEGOTIATION_INFO_SCSV 密码
【发布时间】:2016-05-17 05:51:51
【问题描述】:

Node.js 默认发送TLS_EMPTY_RENEGOTIATION_INFO_SCSV 密码以保护自己免受POODLE attack 的侵害。

我正在尝试通过使用自定义密码列表覆盖 TLS 密码来避免发送此密码(即使这可能会带来安全风险)。

但是,无论我做什么,Node.js 都会不断发送TLS_EMPTY_RENEGOTIATION_INFO_SCSV 密码。我试图故意避免发送此密码来模仿 Firefox/Chrome 的 TLS 协商。

这是我用来修改和检查节点发送的密码的代码:

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);
    }
});

有什么方法可以禁止在 Node.js 中发送此密码?

【问题讨论】:

  • 我认为这与 node.js 没有直接关系,而是与您正在使用的请求模块有关。还是您将问题调试到 node.js 核心?
  • 我不确定这是否正确:"...如果我故意禁用协议降级(通过强制 TLS 1.2",则无需发送此密码。我相信所有TLS 版本会遭受降级攻击,因为 TLS使用 {min-TLS,max-TLS} 版本对。相反,TLS_FALLBACK_SCS 会随着时间的推移而工作,所以它适用于连续连接,而不是真空中的单一连接尝试。
  • @migg 它位于 Node.js 核心中——查看ssl_lib.c#L1472,这似乎是 Node.js 中将 SCSV 密码添加到客户端 Hello 的位置(我可能错了关于该行,但它在该文件中)。 request npm 模块依赖于 Node.js 的 tls 包,它通过 openssl 处理 TLS 连接。 @jww 您可能是对的,它不安全,但是,我仍然想在测试用例中禁用它,并且此时不关心安全隐患。

标签: javascript node.js ssl openssl tls1.2


【解决方案1】:

鉴于此问题与 Node.js 有关,有一种简单的方法可以解决您的问题,而无需深入研究:

在您的 Node.js 进程前面放置一个 Web 代理,让它处理完整的 SSL 连接。在 Node.js 代码本身中,您只会向本地 HTTP 服务器发送请求。

使用 nginx 的示例配置(在 http 指令内):

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;
   }
}

并将nodejs更改为:

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);
    }
});

不幸的是,虽然我确实这样做了,但结果是一样的:

{"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"}

这基本上意味着它可能是标准的 OpenSSL 行为。

可以使用SSL_CTX_set_options在 OpenSSL 中设置一些选项

这里特别有趣的是 SECURE RENEGOTIATION 部分和这个选项:

SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION

允许在 OpenSSL 和未打补丁的客户端或服务器之间进行传统的不安全重新协商。有关详细信息,请参阅安全重新协商部分。

我不确定这是否真的阻止了重新协商密码的发送。 如果此选项实际上是正确的,则可能有一种方法可以修补 Node.js 以使用该选项或使用该选项重新编译 OpenSSL。

当然也可以选择使用未打补丁的旧版本。 据我了解,虽然TLS_EMPTY_RENEGOTIATION_INFO_SCSV 与 POODLE 无关,但来自较旧的修复:

CVE-2009-3555(OpenSSL 公告)2009 年 11 月 5 日:
实施 RFC5746 以解决 SSL/TLS 重新协商中的漏洞。 在 OpenSSL 0.9.8m 中修复(受影响的 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)

现代 Node.js 带有一个静态链接的 OpenSSL,但是不支持 OpenSSL 0.9.8,所以你需要一个旧版本的 Node.js 无论如何......或者使用带有未修补 OpenSSL 的 nginx 东西......

这就是我卡住的地方。 它并不是一个完整的答案,但我认为它至少值得分享。

不过,总而言之,我认为如果您想在不重新编译的情况下使用 nginx 和未打补丁的 OpenSSL,并配置多台服务器,一个用于您想要模仿的每个客户端。

如果您需要在 node 中单独完成,最好的办法是直接在此处修补 OpenSSL 并重新编译 node。

【讨论】:

  • 这是要走的路。我总是让 Ngnix 处理我的 SSL——未来出现问题的风险要小得多。
  • 非常感谢您的详细回答!我想我最终会修补 Node.js 并手动编译它,因为即使在节点 0.8.x 上我也无法避免发送此密码。
【解决方案2】:

似乎 TLS_EMPTY_RENEGOTIATION_INFO 是一个占位符密码套件,执行与扩展“renegotiation_info”相同的功能。此外,OpenSSL 似乎没有设置“renegotiation_info”扩展的方法,并且需要这个空的密码套件。

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

【讨论】:

    猜你喜欢
    • 2018-11-23
    • 2020-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多