仅在浏览器支持SNI时重定向到SSL


20

我有带有mod_ssl的Apache 2.2和带有VirtualHosting的同一IP /端口上的HTTPS中的许多站点,因此客户端必须支持SNI才能连接到这些虚拟主机。

我想通过以下方式配置服务器:

当用户键入www.dummysite.com并且他的浏览器支持 SNI(服务器名称指示)时,任何HTTP请求都将重定向到https://发送HSTS标头的位置。但是,如果浏览器不支持 SNI,则请求将通过HTTP进行处理。

对于仍然运行旧版浏览器的人来说,上述规则实际上是一个后备规则,因为Mozilla和Chrome不会出现此问题,只是为了避免将这些用户留在网站之外。

我想在Apache配置级别执行此重定向,也许使用用户代理上的过滤器。除了确保没有直接的http://引用,否则我不想接触正在运行的应用程序(否则它们表示安全警告)

[编辑](在编辑问题时我忘记问题):启用SNI的用户代理列表是什么?

Answers:


20

由于SNI发生在SSL / TLS握手期间,因此当客户端连接到HTTP时无法检测到浏览器支持。

所以,你是对的。用户代理过滤器是执行此操作的唯一方法。

最大的问题是,您是否要对已知不会监听SNI的浏览器执行黑名单操作,还是要对已知支持该功能的浏览器加入白名单。晦涩或无法使用该网站的新设备似乎是一个破坏交易的事情,因此我想说,白名单可能是更好的选择。

在您的HTTP中<VirtualHost>

# Internet Explorer 7, 8, 9, on Vista or newer
RewriteCond %{HTTP_USER_AGENT} MSIE\s7.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s8.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s9.*Windows\sNT\s6 [OR]
# Chrome on Windows, Mac, Linux
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s6.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Macintosh.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Linux.*Chrome [OR]
# Firefox - we'll just make the assumption that all versions in the wild support:
RewriteCond %{HTTP_USER_AGENT} Gecko.*Firefox
RewriteRule ^/(.*)$ https://ssl.hostname/$1 [R=301]

这也是黑名单选项-请记住,这冒着将不使用SNI的客户端发送到需要SNI的站点的风险,但另一方面,会将诸如IE 10之类的新内容发送给用户地点:

# IE 6
RewriteCond %{HTTP_USER_AGENT} !MSIE\s6
# Windows XP/2003
RewriteCond %{HTTP_USER_AGENT} !Windows\sNT\s5
# etc etc
RewriteRule ^/(.*)$ https://ssl.hostname/$1 [R=301]

那里有很多浏览器。我对表达式的描述一直很松散,并且没有涉及很多浏览器-这可能会变成维护噩梦。

无论您选择哪个选项..祝您好运!


1
大!我使用Opera Mobile(兼容SNI,但未在列表中)进行过测试,并且不重定向。使用我的Firefox,它确实可以重定向!
USR-本地ΕΨΗΕΛΩΝ

6
我建议用模块化从mod_setenvif的BrowserMatch指令,这个解决方案httpd.apache.org/docs/2.2/mod/mod_setenvif.html。使用BrowserMatch设置环境变量support_sni = y,然后编写RewriteCond%{ENV:supports_sni} = y。这样,您可以将SNI检测逻辑重用于可能具有的任何其他RewriteRules。
200_success

@ 200_success好主意!
Shane Madden

8

我的解决方案是这样的:

  # Test if SNI will work and if not redirect to too old browser page
  RewriteCond %{HTTPS} on
  RewriteCond %{SSL:SSL_TLS_SNI} =""
  RewriteRule ^ http://www.example.com/too-old-browser [L,R=307]

如果没有SNI的旧浏览器尝试访问https://www.example.com/ *,那么它将首先在浏览器上引发错误,这是无法避免的,因为直到apache回复了非SNI浏览器,它才知道它要求哪个站点。然后,它重定向到一个页面,告诉用户其浏览器太旧(只要用户单击继续访问网站)。

对于使用新浏览器的用户,我拥有

  #Test if new browser and if so redirect to https
  #new browser is not MSIE 5-8, not Android 0-3
  RewriteCond %{HTTPS} off
  RewriteCond %{HTTP_USER_AGENT} !MSIE\ [5-8]
  RewriteCond %{HTTP_USER_AGENT} !Android.*(Mobile)?\ [0-3]
  RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

这不包括大多数旧的浏览器,包括Vista上的MSIE 5-8(9+仅是Vista / 7,因此支持SNI)。它不是100%(符号被忽略等),但应该适用于大多数情况。少数仍然可以选择接受证书错误。


3

据我所知,实际上没有一个很好的方法可以执行此操作-您可以根据标头有条件地使用mod_rewrite规则或类似条件User-agent,但必须在NON-SSL vhost上:如果浏览器没有支持SNI并转到安全(https://)站点,这将获得老式的Apache行为:“这是我与该IP地址关联的第一个SSL证书-希望它是您想要的!” -如果不是该证书,则浏览器期望您收到一条有关主机名不匹配的错误消息。

基本上,这意味着人们必须访问将重定向他们的非SSL插页页-可能会暴露其请求中发送的所有数据。这可能会破坏交易,也可能不会成为交易破坏器(您说如果它们不支持SNI,无论如何都将它们发送到非SSL站点,所以我认为您不太在意安全性。设计一个需要SSL作为加密或身份验证层的系统,尽管如此,我还是要坚持一点……)

但是,这些都不能阻止某人将安全站点添加为书签-并且,如果他们使用共享书签服务或将其书签还原到网络浏览器不支持SNI的计算机上,他们将返回“ SSL潜在错误”案例。


1

我很想解决以下三种方法之一:

  1. RewriteRule基于User-Agent标题。
  2. <SCRIPT>在非默认VHost 的代码中加载https:// URI ;如果加载成功,则是一些JS可以在HTTPS下重新加载整个页面。
  3. 教我的访问者使用HTTPS Everywhere之类的东西(如果这对他们来说是优先事项),在需要使用HTTPS的页面上强制使用HTTPS,并希望一切最终都能解决。

其中,我个人最喜欢#2,但这涉及修改您网站的代码。


0

只为需要它的人。

如果您有多个主机,并且希望所有主机都在VirtualHosting中启用SSL(并且您为每个主机购买了证书),请尝试使用新主机。 mod_djechelon_ssl

$ cat /etc/apache2/mod_djechelon_ssl.conf 
RewriteEngine on
# Internet Explorer 7, 8, 9, on Vista or newer
RewriteCond %{HTTP_USER_AGENT} MSIE\s7.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s8.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s9.*Windows\sNT\s6 [OR]
# Chrome on Windows, Mac, Linux
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s6.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Macintosh.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Linux.*Chrome [OR]
# Firefox - we'll just make the assumption that all versions in the wild support:
RewriteCond %{HTTP_USER_AGENT} Gecko.*Firefox [OR]
#Safari iThing
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPhone.*Safari [OR]
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPod.*Safari [OR]
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPad.*Safari [OR]
RewriteRule ^/(.*)$ https://%{HTTP_HOST}/$1 [R=permanent,L]

用法:

<VirtualHost ip:80>
ServerName www.yourhost.com

Include /path/to/mod_djechelon_ssl.conf

[plain old Apache directives]
</VirtualHost>

<VirtualHost ip:443>
ServerName www.yourhost.com

[SSL-related directives]

[Copy and paste directives from above host]
</VirtualHost>

0

正如我在此处发布的,您只能需要SNI支持之前对其进行测试。也就是说,您不能强迫用户使用SNI HTTPS,然后如果他们不支持它就回退,因为他们将收到这样的错误(来自Windows XP上的Chrome),无法继续。

因此(不幸的是)用户实际上必须从不安全的HTTP连接开始,然后仅在支持SNI的情况下才进行升级。

您可以通过以下方式检测SNI支持:

  1. 远程脚本
    在纯HTTP页面上,<script>从目标SNI HTTPS服务器加载a ,如果脚本加载并正确运行,则说明浏览器支持SNI。

  2. 跨域AJAX(CORS)
    与选项1相似,您可以尝试执行从HTTP页面到HTTPS的跨域AJAX请求,但是请注意,CORS 仅对有限的浏览器支持

  3. 嗅探用户代理
    这可能是最不可靠的方法,您将需要确定是否拥有不支持的浏览器(和操作系统)黑名单,或支持该功能的已知系统白名单。

    我们知道Windows XP及以下版本的IE,Chrome和Opera的所有版本均不支持SNI。有关支持的浏览器的完整列表,请参见CanIUse.com

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.