网站建设

利用HSTS安全协议柔性解决全站HTTPS的兼容性问题

Jager · 11月22日 · 2016年 · · 14530次已读

导读:目前,很多站都开始实现 HTTPS 了,而且其中的大部分强迫症站长还会开启强制 HTTPS 机制,对于网站的 HTTP 请求全部 301 跳转到 HTTPS,从而实现全站 HTTPS。这明显是一个粗暴的做法,下面张戈博客就分享一下目前正在使用的柔性做法,告别粗暴。

利用HSTS安全协议柔性解决全站HTTPS的兼容性问题

一、HSTS 协议

这里我们要借助一个新的安全协议:HSTS

HSTS(HTTP Strict Transport Security)国际互联网工程组织 IETE 正在推行一种新的 Web 安全协议,作用是强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。

主要目的是为了解决 HTTPS 网站首次请求时使用的是未加密的 HTTP 协议,也就说用户一般访问我们的网站都是直接在浏览器输入域名,比如 zhang.ge,然后我们的服务器检测到是 HTTP 请求,就 301 跳转到 HTTPS 页面。那么前半程采用的就是未加密的 HTTP 请求,同样存在被劫持的可能,那么 HTTPS 说好的安全性也就大打折扣了!

在我看来,HSTS 还有另外一层好处:增强网站的兼容性。

以往分享的全站 HTTPS 都是采用 301 强制性跳转,而且还会区分下低版本 IE、不支持 HTTPS 的搜索引擎来忽略 301 跳转,很明显这样做无法照顾到所有情况。那么如果是用 HSTS 呢?

采用 HSTS 后,支持这个协议的浏览器会自动跳转到 HTTPS 页面,返回码为 307:利用HSTS安全协议柔性解决全站HTTPS的兼容性问题

而不支持 HSTS 的浏览器访问我们的网站,则不会产生跳转,从而提高了兼容性。这个机制对于不支持 HTTPS 的搜索引擎来说是非常友好的做法了!

二、开启 HSTS

开启 HSTS 很简单,只要在我们网站的响应头里面新增 HSTS 即可,下面简单说下

①、Nginx 服务器

只需要在站点 server 模块内插入如下配置并重启:

server {
    listen 443 ssl http2; 
    server_name zhang.ge;
    # 直接在 server 插入测试居然不生效,最后发现要在 location ~ *php 内插入:
    location ~ [^/]\.php(/|$) {
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
        # 以下略...

②、Apache 服务器

Apache 如下配置并重启:

# 先在 Apache 加载 mod_header 库,一般位于 httpd.conf 文件,自行搜索 mod_headers 并取消注释
LoadModule headers_module modules/mod_headers.so

#然后在站点 VirtualHost 里面插入 HSTS 响应头信息,比如:
<VirtualHost *.*.*.*:443>
    Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
</VirtualHost>

③、LigHttpd

将下述配置增加到你的 Lighttpd 配置文件(一般是 /etc/lighttpd/lighttpd.conf)并重启:

server.modules += ( "mod_setenv" )
$HTTP["scheme"] == "https" {
    setenv.add-response-header  = ( "Strict-Transport-Security" => "max-age=63072000; includeSubdomains; preload")
}

④、通用方法

如果你用的虚拟主机,或者不会折腾 WEB 软件,那么可以采用更简单的通用方法。原理很简单,通过代码来新增响应头即可,这里只分享一下 php 的做法,其他语言自行参考:

将如下代码插入到网站根目录的 index.php 即可:

header("Strict-Transport-Security: max-age=63072000; includeSubdomains; preload");

三、相对链接

当然,为了兼容不支持 HTTPS 的客户端,我们还需要将网站的所有超链接都改成相对模式:

比如,正常的页面链接如下所示:

<a href="/goto/aHR0cDovL3d3dy5kb21haW4uY29tLzEuaHRtbA==" target="_blank" target="_blank">描文本</a>

改成相对模式:

<a href="//www.domain.com/1.html" target="_blank">描文本</a>

好处就是,不管是 HTTP 还是 HTTPS 请求,页面中的地址都是和请求协议保持一致,避免出现页面是 HTTP,而页面中的链接却是 HTTPS 的情况,那么前面的做法也就没了意义。

如何修改为相对模式,估计有同学又玩不转了。万变不离其宗,和以前纯代码启用七牛 CDN 一样!

直接粗暴替换前台输出的代码即可:

//将所有超链接改为相对模式
if(!is_admin()){
       ob_start("rewrite_urls");
     }
function rewrite_urls($buffer){
	$buffer= preg_replace('/("|\')http(s|):\/\/([^"\']*?)'.$_SERVER["HTTP_HOST"].'/i','$1//$3'.$_SERVER["HTTP_HOST"],$buffer);
	return $buffer;
}

将以上代码新增到 WordPress 主题的 functions.php 中即可。以上代码只会替换和网站主域名有关系的超链接,八竿子打不着的外部超链接就不管了,有需求自行参考解决。

四、提交 HSTS

上文已介绍了 HSTS,主要是为了解决 HTTP 请求 301 跳转到 HTTPS 这个过程被劫持问题,而实际上就算加上 HSTS 响应头,用户请求的前半程依然是 HTTP,并没有什么 L 用。

提出这个协议的砖家们就想出了一个解决办法:将支持 HSTS 的网站全部加入一个 Preload 的清单,支持 HSTS 协议的浏览器请求网站前会查询当前网站是否在清单中,如果是那么直接转换为 HTTPS 请求!从而解决前半程为 HTTP 的问题(不专业,但说人话。。。)。

那么,如果我们的网站启用了 HSTS,还得将网站提交到这个 Preload 清单才行了

提交地址:https://hstspreload.appspot.com/  (需要扶墙访问)

提交直到批准,我们的网站必须强制 301 跳转到 HTTPS,否则无法通过,完成审核后再取消 301 即可。

当然,提交后会显示正在提交到 preload list,快的话两三天,慢的话一两个月都是有可能的:利用HSTS安全协议柔性解决全站HTTPS的兼容性问题

好了,罗里吧嗦分享了一大堆,自行参考吧!

20170205 最新补充:经过漫长的等待,偶然查询发现已经是 preload 状态了,可真不容易:利用HSTS安全协议柔性解决全站HTTPS的兼容性问题

82 条回应
  1. 狂放 2017-6-14 · 21:25

    用外部链接加载静态资源的我就木有办法惹

    • avatar
      Jager 2017-6-15 · 13:38

      把这些资源下载到服务器 本地。。或者找支持https的资源。。。

  2. NVZA 2017-7-8 · 13:04

    你好。博主,设置好https后,http全部网址打不开,iis环境怎么实现http定向https,求分享方法

  3. 喻城 2017-7-29 · 17:36

    宝塔面板可以吗,输入特效不错 :grin:

  4. 茗彩 2017-8-18 · 23:41

    学习了,很有深度。

  5. 码农&西瓜 2017-9-5 · 15:43

    之前看到一篇文章:从HTTP到HTTPS再到HSTS,非常不错。

  6. 云落 2017-10-11 · 22:27

    上了https之后又下来了。。。。

  7. 启明 2017-10-14 · 0:04

    呐,我现在也启用了这个,可以发现在访问我的wp后台的时候呢,直接输入域名打开的还是http,就是错误页面了,非要手动输入https://xxxx.com/wp-admin 才可以正确访问,这是怎么回事呢!

    • avatar
      Jager 2017-10-17 · 22:02

      后台常规设置中设置为https的地址

  8. 启明 2017-10-14 · 0:42

    我又回来了,发现几个问题这个hsts配置 能否设置不让二级域名也走https?
    关于提交收录到浏览器内置hsts的情况,是需要浏览器支持这个功能的才行的,想一些国产的部分浏览器不支持的话也不好用哦,
    比如我发现手机浏览器上就有不少不支持的,拿UC来举例,输入我的域名,不添加http或者https 他会转到搜索页面...
    但是Uc的ipad版本倒是可以正确按照301转向到https来正常访问,但是又来了..在进入后台的时候直输域名会转到http而不是按照301的规则从http转到https ,从而显示错误页面,
    这个 我在我的手机和平板都测试了.....无F uck说!

    • avatar
      Jager 2017-10-17 · 22:03

      HSTS所有域名都要走https。。

  9. zhy 2017-12-1 · 10:34

    那如果nginx或apache前面还有阿里云的slb,这样设置有效吗?

  10. 枫少 2017-12-18 · 21:45

    看样子,百度分享需要下载到本地才能在文章页有小绿锁啊

  11. 明月登楼 2018-4-6 · 20:12

    受本文的影响,我也把imydl.com和imydl.tech都加入预加载表了!还都通过了!

  12. hi 2019-2-15 · 18:14

    HSTS首先要访问过https,意思就是拟首次访问不能http,如果是http还是得靠301

    • avatar
      Jager 2019-2-16 · 19:41

      申请加入Preload 清单后,浏览器在访问时自动使用https协议,这个谷歌内核的浏览器应该都是支持的。

  13. mimi 2019-12-20 · 19:01

    我检测的时候得到警告:
    HTTP上
    不必要的HSTS标头
    http://btsybt.com上的HTTP页面发送HSTS标头。这对HTTP无效,应将其删除。
    这个警告 有必要想办法解除吗