避免在 TLS Client Hello 中发送 TLS_EMPTY_RENEGOTIATION_INFO_SCSV 密码
Avoid sending TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher in TLS Client Hello
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 进程前面放置一个网络代理,让它处理完整的 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
Allow legacy insecure renegotiation between OpenSSL and unpatched clients or servers. See the SECURE RENEGOTIATION section for more details.
我不确定这是否真的阻止了重新协商密码的发送。
如果此选项实际上是正确的,则可能有一种方法可以修补 Node.js 以使用该选项或使用该选项重新编译 OpenSSL。
当然也可以选择使用未打补丁的旧版本。
据我了解,虽然 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
与 POODLE 无关,但来自较旧的修复:
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)
现代 Node.js 带有静态链接的 OpenSSL 但是不支持 OpenSSL 0.9.8,因此您需要旧版本的 Node.js 无论如何......或者使用 nginx 东西未打补丁的 OpenSSL...
这就是我被困的地方。
这不是一个完整的答案,但我认为它至少值得分享。
总而言之,我认为如果您想在不重新编译的情况下执行此操作,请将 nginx 与未打补丁的 OpenSSL 一起使用,并配置多个服务器,一个用于您要模拟的每个客户端。
如果您需要在节点中单独完成此操作,最好的办法是直接在那里修补 OpenSSL 并重新编译节点。
似乎TLS_EMPTY_RENEGOTIATION_INFO是一个占位符密码套件,其功能与扩展“renegotiation_info”相同。此外,OpenSSL 似乎无法设置“renegotiation_info”扩展名,因此需要这个空的密码套件。
来源:
资料来源:RFC 5746 Section-3.3
Node.js 默认发送 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
密码以保护自己免受 POODLE attack.
我试图通过使用自定义密码列表覆盖 TLS 密码来避免发送此密码(尽管这可能会带来安全风险)。
但是,无论我做什么,Node.js 都会继续发送 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
密码。我试图故意避免发送此密码来模仿 Firefox/Chrome.
这是我用来修改和检查节点发送的密码的代码:
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 进程前面放置一个网络代理,让它处理完整的 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
Allow legacy insecure renegotiation between OpenSSL and unpatched clients or servers. See the SECURE RENEGOTIATION section for more details.
我不确定这是否真的阻止了重新协商密码的发送。 如果此选项实际上是正确的,则可能有一种方法可以修补 Node.js 以使用该选项或使用该选项重新编译 OpenSSL。
当然也可以选择使用未打补丁的旧版本。
据我了解,虽然 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
与 POODLE 无关,但来自较旧的修复:
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)
现代 Node.js 带有静态链接的 OpenSSL 但是不支持 OpenSSL 0.9.8,因此您需要旧版本的 Node.js 无论如何......或者使用 nginx 东西未打补丁的 OpenSSL...
这就是我被困的地方。 这不是一个完整的答案,但我认为它至少值得分享。
总而言之,我认为如果您想在不重新编译的情况下执行此操作,请将 nginx 与未打补丁的 OpenSSL 一起使用,并配置多个服务器,一个用于您要模拟的每个客户端。
如果您需要在节点中单独完成此操作,最好的办法是直接在那里修补 OpenSSL 并重新编译节点。
似乎TLS_EMPTY_RENEGOTIATION_INFO是一个占位符密码套件,其功能与扩展“renegotiation_info”相同。此外,OpenSSL 似乎无法设置“renegotiation_info”扩展名,因此需要这个空的密码套件。
来源:
资料来源:RFC 5746 Section-3.3