浅谈SNI 兼容性导致 HTTPS 出错问题

昨天快要下班时候,售后代表突然甩出了这样一个问题。 (本文域名、证书信息、IP手动马赛克了)

Host name ‘www.xxxx.com’ does not match the certificate subject provided by the peer (CN=yyyy.com)

大致就是说,要访问的域名和其配置的证书不匹配,这….

你要说我有问题,不好意思,我第一时间脱口而出:我配置的证书没有问题!

当然了,得有理有据啊,其实我那时也不知道怎么会出现这样的问题。 配置文件 配置文件,冇问题(就不贴配置了) wget debug 我尝试用wget的debug看看情况:wget --debug www.xxxx.com

返回的过程以及结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
DEBUG output created by Wget 1.17.1 on linux-gnu.

Reading HSTS entries from /root/.wget-hsts
URI encoding = ‘UTF-8’
--2018-06-19 18:10:46--  http://www.xxxx.com/
Resolving www.xxxx.com (www.xxxx.com)... xxx.xxx.xxx.xxx
Caching www.xxxx.com => xxx.xxx.xxx.xxx
Connecting to www.xxxx.com (www.xxxx.com)|xxx.xxx.xxx.xxx|:80... connected.
Created socket 3.
Releasing 0x0000561137af5380 (new refcount 1).

---request begin---
GET / HTTP/1.1
User-Agent: Wget/1.17.1 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: www.xxxx.com
Connection: Keep-Alive

---request end---
HTTP request sent, awaiting response...
---response begin---
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0
Date: Tue, 19 Jun 2018 18:10:47 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: https://www.xxxx.com/

---response end---
301 Moved Permanently
Registered socket 3 for persistent reuse.
Location: https://www.xxxx.com/ [following]
Skipping 185 bytes of body: [<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.14.0</center>
</body>
</html>
] done.
URI content encoding = None
--2018-06-19 18:10:47--  https://www.xxxx.com/
Found www.xxxx.com in host_name_addresses_map (0x561137af5380)
Connecting to www.xxxx.com (www.xxxx.com)|xxx.xxx.xxx.xxx|:443... connected.
Created socket 4.
Releasing 0x0000561137af5380 (new refcount 1).
Initiating SSL handshake.
Handshake successful; connected socket 4 to SSL handle 0x0000561137b15ef0
certificate:
  subject: CN=www.xxxx.com
  issuer:  CN=TrustAsia TLS RSA CA,OU=Domain Validated SSL,O=TrustAsia Technolog
ies\\, Inc.,C=CN
X509 certificate successfully verified and matches host www.xxxx.com

---request begin---
GET / HTTP/1.1
User-Agent: Wget/1.17.1 (linux-gnu)
Accept: */*
Accept-Encoding: identity
Host: www.xxxx.com
Connection: Keep-Alive

---request end---
HTTP request sent, awaiting response...
---response begin---
HTTP/1.1 404 Not Found
Server: nginx/1.14.0
Date: Tue, 19 Jun 2018 10:10:47 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Vary: Accept-Encoding

---response end---
404 Not Found
Disabling further reuse of socket 3.
Closed fd 3
Registered socket 4 for persistent reuse.
Skipping 169 bytes of body: [<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.14.0</center>
</body>
</html>
] done.
2018-06-19 18:10:47 ERROR 404: Not Found.

Saving HSTS entries to /root/.wget-hsts

连wget访问都OK的呀( •̀ ω •́ )y

openssl检测 openssl可以查看指定域名的证书的 openssl s_client -showcerts -connect www.xxxx.com:443 返回过程以及结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = CN, O = "TrustAsia Technologies, Inc.", OU = Domain Validated SSL, CN = TrustAsia TLS RSA CA
verify return:1
depth=0 CN = yyyy.com
verify return:1
---
Certificate chain
 0 s:/CN=yyyy.com
   i:/C=CN/O=TrustAsia Technologies, Inc./OU=Domain Validated SSL/CN=TrustAsia TLS RSA CA
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
 1 s:/C=CN/O=TrustAsia Technologies, Inc./OU=Domain Validated SSL/CN=TrustAsia TLS RSA CA
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=yyyy.com
issuer=/C=CN/O=TrustAsia Technologies, Inc./OU=Domain Validated SSL/CN=TrustAsia TLS RSA CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3311 bytes and written 431 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: 21575C9223F6027E4CE5CBC01F8B6B0ED6F270583D9B319894DC16EF240F951C
    Session-ID-ctx:
    Master-Key: 5428CBE1D05323AA4776D2EDAB61AA7ACA0FDD930297D8E1653F020656E83CD11E2752AF64D0CF997A740578B619DB04
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - ff 06 24 d7 7f 2c eb 94-e4 04 22 00 df b8 cf 0b   ..$..,....".....
    0010 - 8c 19 52 50 4b e0 42 5e-d2 4a 1c 94 9e 12 e4 e3   ..RPK.B^.J......
    0020 - 89 86 42 9e d4 d3 7c 2b-f1 f6 25 d4 6e 68 ce 59   ..B...|+..%.nh.Y
    0030 - 7a 6f db ed 8d 64 1e 2c-35 8e 5f a0 0d 26 d1 e7   zo...d.,5._..&..
    0040 - c0 42 07 46 04 bd 7e d8-4d 85 8e b7 95 ad c0 6a   .B.F..~.M......j
    0050 - 54 7b cc b3 b9 33 8e 4b-4b 5b ed e1 d8 23 db 25   T{...3.KK[...#.%
    0060 - 3a a3 38 a9 50 a9 da 7d-71 ed f5 32 03 53 1d a9   :.8.P..}q..2.S..
    0070 - 90 84 e8 b0 e2 e0 c6 33-2c 32 9b 0f 27 72 69 15   .......3,2..'ri.
    0080 - 43 62 82 6e 2f e7 78 21-4e 43 61 0c 1b f1 75 55   Cb.n/.x!NCa...uU
    0090 - 92 14 a4 d8 2a a4 28 a4-c9 ca 3b f1 15 a5 ca b1   ....*.(...;.....
    00a0 - 8e 10 68 15 e8 73 c8 fc-ac 04 a1 79 85 30 f5 dd   ..h..s.....y.0..

    Start Time: 1529404197
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
closed

这里发现返回的证书就有问题了。

ssllabsSSL Labs检测一下,发现问题了!

Certificate #2: RSA 2048 bits (SHA256withRSA) No SNI

等等,这里怎么有第二张证书,没有配置的啊! 这张证书和访问的域名是不匹配的,注意No SNI 往下看:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Android 2.3.7   No SNI 2	Incorrect certificate because this client doesn't support SNI
RSA 2048 (SHA256)   |  TLS 1.0  |  TLS_RSA_WITH_AES_128_CBC_SHA`

IE 8 / XP   No FS 1	  No SNI 2	Incorrect certificate because this client doesn't support SNI
RSA 2048 (SHA256)   |  TLS 1.0  |  TLS_RSA_WITH_3DES_EDE_CBC_SHA

Java 6u45   No SNI 2	Incorrect certificate because this client doesn't support SNI
RSA 2048 (SHA256)   |  TLS 1.0  |  TLS_RSA_WITH_AES_128_CBC_SHA

IE 6 / XP   No FS 1	  No SNI 2	Protocol mismatch (not simulated)
(1) Clients that do not support Forward Secrecy (FS) are excluded when determining support for it.
(2) No support for virtual SSL hosting (SNI). Connects to the default site if the server uses SNI.
(3) Only first connection attempt simulated. Browsers sometimes retry with a lower protocol version.
(R) Denotes a reference browser or client, with which we expect better effective security.
(All) We use defaults, but some platforms do not use their best protocols and features (e.g., Java 6 & 7, older IE).
(All) Certificate trust is not checked in handshake simulation, we only perform TLS handshake.

看到这里似乎知道这个错误好像跟SNI关联性很大啊。

那么SNI是个啥呀!

SNI背景介绍

复制粘贴中… 随着 IPv4 地址的短缺,为了让多个域名复用一个 IP 地址,在 HTTP 服务器上引入了虚拟主机的概念。服务器可以根据客户端请求中不同的 Host,将请求分配给不同的域名(虚拟主机)来处理。在一个被多个域名(虚拟主机)共享 IP 的 HTTPS 服务器中,当浏览器访问一个 HTTPS 站点时,会首先与服务器建立 SSL 连接。建立 SSL 连接的第一步是请求服务器的证书。服务器在发送证书时,不知道浏览器访问的是哪个域名,所以不能根据不同域名发送不同的证书。

(附上偷的一张图片 我自己画了一张图 SNI.png

SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的 SSL/TLS 扩展。它的工作原理是:在连接到服务器建立 SSL 连接之前,先发送要访问站点的域名(Hostname),这样服务器会根据这个域名返回一个合适的证书。

目前,大多数操作系统和浏览器都已经很好地支持 SNI 扩展。OpenSSL 0.9.8 已经内置这一功能,新版的 Nginx 也支持 SNI。

如果客户端不支持 SNI,可能会出现如下现象:

  • 在手机 App 客户端,iOS 客户端可以正常访问,而 Android 客户端无法正常打开。
  • 浏览器打开网站,显示证书不可信。

SNI 兼容性

注意:SNI 兼容 TLS1.0 及以上协议,但不被 SSL 支持。

SNI 支持以下 桌面版浏览器

  • Chrome 5及以上版本
  • Chrome 6及以上版本(Windows XP)
  • Firefox 2及以上版本
  • IE 7及以上版本(运行在 Windows Vista/Server 2008 及以上版本系统中,在 XP 系统中任何版本的 IE 浏览器都不支持 SNI)
  • Konqueror 4.7 及以上版本
  • Opera 8 及以上版本
  • Safari 3.0 on Windows Vista/Server 2008 及以上版本, or Mac OS X 10.5.6 及以上版本

SNI 支持以下

  • GNU TLS
  • Java 7 及以上版本,仅作为客户端
  • HTTP client 4.3.2 及以上版本
  • libcurl 7.18.1 及以上版本
  • NSS 3.1.1 及以上版本
  • OpenSSL 0.9.8j 及以上版本
  • OpenSSL 0.9.8f 及以上版本,需配置 flag
  • Qt 4.8 及以上版本

SNI 支持以下 手机端浏览器

  • Android Browser on 3.0 Honeycomb 及以上版本
  • iOS Safari on iOS 4 及以上版本
  • Windows Phone 7 及以上版本

SNI 支持以下 服务器

  • Apache 2.2.12 及以上版本
  • Apache Traffic Server 3.2.0 及以上版本
  • HAProxy 1.5 及以上版本
  • IIS 8.0 及以上版本
  • lighttpd 1.4.24 及以上版本
  • LiteSpeed 4.1 及以上版本
  • nginx 0.5.32 及以上版本

SNI 支持以下 命令行

  • cURL 7.18.1 及以上版本
  • wget 1.14 及以上版本

关于SNI的一些说明

  • 查看Nginx是否配置了SNI的支持:

/usr/local/nginx/sbin/nginx -V 返回结果

1
2
3
4
5
nginx version: nginx/1.14.0
built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
built with OpenSSL 1.0.2n  7 Dec 2017
TLS SNI support enabled
configure arguments: --user=nginx --group=nginx --add-module=../ngx_brotli --add-module=../nginx-ct-1.3.2 --with-openssl=../openssl --with-http_v2_module --with-http_ssl_module --with-http_gzip_static_module --with-http_realip_module

可以看到enable,配置了支持的。

  • openssl支持SNI特性 可以带上-servername 域名进行检测,像这样: openssl s_client -servername www.xxxx.com -showcerts -connect www.xxxx.com:443

问题总结 问题的锅在于客户端不支持 SNI 扩展,询问下来,对方是用eclipse本地测试。

对于不支持 SNI 的客户端

  • 建议升级或使用新版本的浏览器(如 Chrome、Firefox 等)。
  • 如果是微信、支付宝第三方回调,需要让其调用源站 IP,绕过 Web 应用防火墙。

文章参考: SNI 兼容性导致 HTTPS 访问异常(服务器证书不可信)

Licensed under CC BY-NC-SA 4.0
最后更新于 May 08, 2019 16:47 UTC
点击刷新🚌