我需要使用 SSL SNI,但不幸的是,从最近的 Cloudflare 博客文章来看,只有 90% 的网络支持它。我如何(例如,使用 nginx)检测客户端是否支持 SNI 并提供/重定向到网站的 HTTP 版本?这可能吗?否则,我怎样才能避免使用 SNI 丢失 10% 的流量?是否可以假设,如果没有有效的证书,我将无法将 HTTPS 流量重定向到 HTTP,因此此请求是不可能的?
谢谢。
答案1
如果您想从一开始就提供 HTTPS,则必须从一开始就提供客户端接受的证书。否则,客户端将不会接受 SSL 连接,您将无法将客户端重定向到其他站点或仅 HTTP 版本。这意味着要支持这种情况,您
- 要么需要拥有一个包含所有域的证书,以便您可以为非 SNI 客户端提供适当的证书。但在这种情况下,您根本不需要 SNI。
- 或者您必须安装一些与您的大多数名称不匹配的默认证书。在这种情况下,如果客户端接受此不良证书,您只能向客户端提供不同的页面或重定向它。
如果您不需要从一开始就使用 HTTPS,即如果客户端通常首先使用纯 HTTP 连接,那么您可以尝试检测 SNI 支持,以便稍后重定向客户端。这可以通过从 HTTPS 站点包含图像、一些 JavaScript 或类似内容来完成,如果加载成功,那么您就知道客户端支持 SNI 或忽略证书错误。
当然,这会让一切都暴露在中间人攻击之下,因为中间人所要做的就是提供一些不同的证书或使 HTTPS 完全不可用,因为在这种情况下,您永远不会尝试将连接升级到 HTTPS。此外,如果中间人确实支持 SNI,这还可以用来使客户端看起来支持 SNI。不仅非 SNI 客户端会受到影响,而且支持 SNI 的客户端也只能被拦截。因此,虽然这在理论上是可能的,但不建议这样做,因为您可以简单地使用中间人来做所有事情,从而使使用 HTTPS 的主要目的变得毫无意义。
答案2
正如我在 StackOverflow 上发表的那样,你只能测试 SNI 支持事先的要求它。也就是说,您不能强迫用户使用 SNI HTTPS,如果他们不支持,则回退,因为他们将收到这样的错误(来自 Windows XP 上的 Chrome),无法继续。
因此(不幸的是)用户必须实际上通过不安全的 HTTP 连接开始,然后只有在支持 SNI 时才能升级。
您可以通过以下方式检测 SNI 支持:
远程脚本
从您的纯 HTTP 页面,从您的目标 SNI HTTPS 服务器加载<script>
,如果脚本加载并正确运行,则表明浏览器支持 SNI。跨域 AJAX (CORS)
与选项 1 类似,你可以尝试从 HTTP 页面向 HTTPS 执行跨域 AJAX 请求,但请注意,CORS 具有仅对浏览器提供有限的支持。嗅探用户代理
这可能是最不可靠的方法,您需要在已知不支持它的浏览器(和操作系统)的黑名单和已知支持的系统的白名单之间做出选择。我们知道 Windows XP 及以下版本的所有 IE、Chrome 和 Opera 版本均不支持 SNI。请参阅CanIUse.com 提供受支持浏览器的完整列表。