CloudFlare 的博客文章调试、测试和使用 HTTP/2 的工具提到了这种方法来找出给定服务器支持哪些协议:
$ openssl s_client -connect www.cloudflare.com:443 -nextprotoneg ''
CONNECTED(00000003)
Protocols advertised by server: h2, spdy/3.1, http/1.1
这种方法似乎对我测试过的许多域都有效。但是,(启用 HTTP/2 的)域benchmarkjs.com
产生的输出看起来完全不同,我不知道为什么:
$ openssl s_client -connect benchmarkjs.com:443 -nextprotoneg ''
CONNECTED(00000003)
depth=0 C = US, ST = Washington, L = Seattle, O = Odin, OU = Plesk, CN = Plesk, emailAddress = [email protected]
verify error:num=18:self signed certificate
verify return:1
depth=0 C = US, ST = Washington, L = Seattle, O = Odin, OU = Plesk, CN = Plesk, emailAddress = [email protected]
verify return:1
---
Certificate chain
0 s:/C=US/ST=Washington/L=Seattle/O=Odin/OU=Plesk/CN=Plesk/[email protected]
i:/C=US/ST=Washington/L=Seattle/O=Odin/OU=Plesk/CN=Plesk/[email protected]
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDfTCCAmUCBFXIlUswDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMRMw
EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMQ0wCwYDVQQKDARP
ZGluMQ4wDAYDVQQLDAVQbGVzazEOMAwGA1UEAwwFUGxlc2sxHTAbBgkqhkiG9w0B
CQEWDmluZm9AcGxlc2suY29tMB4XDTE1MDgxMDEyMTMwMFoXDTE2MDgwOTEyMTMw
MFowgYIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH
DAdTZWF0dGxlMQ0wCwYDVQQKDARPZGluMQ4wDAYDVQQLDAVQbGVzazEOMAwGA1UE
AwwFUGxlc2sxHTAbBgkqhkiG9w0BCQEWDmluZm9AcGxlc2suY29tMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvLgc48tQHJaVZj4btZhGySqNlQeXRti8
Nansm15wJXS45EIFx6lYqFbDjD22VBwwDrILNZBY9BiKLV+VsZYuu1sOK+Ctatww
gcOf4UB5pT6kOP48vBJlsaxqf+kCQpiVBniliLs7yAxl3BjjzQBluI4DUMXGpsuD
u2tBi/fsUV6QRBzsebQ6cH5cZ9XPGiirPyFe7yNNbXurooYmeD1YjEH6qYIPhU9O
lTOU9rssii8ShmHL/X54ekIqZ9y5b3eD+Dhp45/WjxXcgSxWZHx8SYcKndiSOJFa
KUXlBKREmvH1nQLtp1BdCH6BiLPXaMMcRPY1BUhLSLt6M68B5vRDqQIDAQABMA0G
CSqGSIb3DQEBCwUAA4IBAQAvTuPoc0RYHXeHW61s6PPC7pRtp1t+roknJPa/po1P
vFtINtR9vuCfeUfS7K8ryfduBkX/km8qjCxjNvjQsJdya5mfMdPzW4AabJs06qUp
h5PPk2ER1OcPzraemcHHL8gzStVk3n9C23ZRAwZaEIsgnOQ58YGf4eyrzKXBClfL
NPL28cQQH5+d4lIsQc7B9d0OiiUQNTBfVD/CA67SPvc/f6Xq7ARdrc44GhA03cUN
0Vr60j2kv9IkdSnRPtt40R8AD97bKu9E/jOJCrSOfCbB3Qz1Pdm2lZqSEnoHdlbg
2KY8HrvoEXvuPUNXRIQEhpR7f9y70iBPORs0PbuC+qDf
-----END CERTIFICATE-----
subject=/C=US/ST=Washington/L=Seattle/O=Odin/OU=Plesk/CN=Plesk/[email protected]
issuer=/C=US/ST=Washington/L=Seattle/O=Odin/OU=Plesk/CN=Plesk/[email protected]
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1409 bytes and written 448 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: C0474D42297C21B374C558E698A69EEBBFF537B35E6365D31B4D7AA2CD4192AD
Session-ID-ctx:
Master-Key: 2292DF581DAE3CE4A4E8CFF5A351A3F4E99602A7DCDBD090998C38926498743B4E3A808A1C4505136D51FC847F0153A6
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1455103745
Timeout : 300 (sec)
Verify return code: 18 (self signed certificate)
---
请注意,它不会返回提示;您已连接到服务器,现在可以输入 egGET / HTTP/1.1
然后输入Host: benchmarkjs.com
。
因此,我的两个问题是:
- 什么不同
benchmarkjs.com
使得该技术无法发挥作用? - 是否可以使用
openssl s_client
通用方法检测 HTTP/2 支持,即使对于该域也有效?
答案1
我的看法:openssl s_client
的手册指出:
-nextprotoneg 协议
启用下一个协议协商 TLS扩大并提供客户端应宣传支持的以逗号分隔的协议名称列表。列表应首先包含最需要的协议。协议名称是可打印的 ASCII 字符串,例如“http/1.1”或“spdy/3”。空的协议列表将被特殊处理,并将导致客户端宣传对 TLS 扩展的支持,但在收到包含服务器支持的协议列表的 ServerHello 后立即断开连接。
(重点是我的。)
因此,我认为终止您请求的 SSL/TLS 引擎仅仅benchmarkjs.com
不支持该 Next Protocol Negotiation 扩展或出于某种原因将其禁用。因此,在您的测试中,openssl s_client
命令宣传它支持 NPN,但服务器对此视而不见。握手仍然顺利通过,因为扩展似乎不是必需的(或至少被 认为是必需的openssl
),并且您获得了连接的 TLS 隧道。
至于实施可靠检查,恐怕由于openssl s_client
显然没有任何命令行选项可以强制它立即终止会话,因此您必须更深入地编写自己的程序链接以libssl
实现所需的行为。 (我个人会在去使用crypto/tls
因为它的标准库包,所以编程并不比 JavaScript 难,但会生成一个静态链接程序,这意味着无需动脑筋即可部署。)