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

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

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

问题描述

在接入 Web 应用防火墙后,如果您出现 HTTPS 访问异常问题,可能是由于客户端不支持 SNI 导致的。

当使用不支持 SNI 的浏览器访问 Web 应用防火墙的网站时,Web 应用防火墙因不知道客户端请求的是哪个域名,无法调取对应的虚拟主机证书来跟客户端交互,只能使用内置的一个缺省证书去跟客户端握手,这时在客户端浏览器上会提示“服务器证书不可信”。

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

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

解决方案

您可以在客户端抓 SSL 握手的报文,来判断客户端是否支持 SNI。以 Chrome 浏览器访问阿里云官网为例。

若在 Client Hello 报文里可以看到 SNI 扩展,则表示客户端支持 SNI 扩展。

否则,客户端不支持 SNI 扩展。对于不支持 SNI 的客户端,

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

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 及以上版本