在上一篇文章中我们已经实现了本地 node 服务使用 https 访问了, 看上一篇文章 效果可以看如下:
但是如果我们现在使用 http 来访问的话, 访问不了. 如下图所示:
因此我现在首先要做的是使用 nginx 配置下, 当用户在浏览器下输入 http 请求的时候使用 nginx 重定向到 https 下即可. 因此我们现在需要做一个简单的 nginx 重定向功能. 想要深入了解 nginx 重定向功能, 可以看这篇文章.
因此在我们的 nginx 中需要加如下重定向配置:
- server {
- listen xxx.abc.com;
- server_name xxx.abc.com;
- rewrite ^/(.*)$ https://$host$1 permanent;
- }
因此 nginx 主要的配置代码如下:
- server {
- listen xxx.abc.com;
- server_name xxx.abc.com;
- rewrite ^/(.*)$ https://$host$1 permanent;
- }
- server {
- listen 443 ssl;
- server_name xxx.abc.com;
- ssl_certificate cert/server.crt;
- ssl_certificate_key cert/server.key;
- ssl_session_cache shared:SSL:1m;
- ssl_session_timeout 5m;
- ssl_ciphers HIGH:!aNULL:!MD5;
- ssl_prefer_server_ciphers on;
- location / {
- proxy_pass http://localhost:3001;
- }
- }
如上配置后, 我们需要重新启动下 nginx 即可生效, 我们在浏览器下输入域名 http://xxx.abc.com 后 会自动重定向到 https://xxx.abc.com/ 了, 我们再来看下 我们网络上的请求有 2 个请求, 如下所示:
如上请求可以看到, 浏览器首先会向网站发起一次 http 请求 (http://xxx.abc.com), 在得到一个重定向响应后, 再会发起一次 https 请求并得到最终的响应内容. 对用户来讲, 它的操作是透明的, 用户体验也是不错的, 但是在 https 链接之前会存在一次明文的 http 请求和重定向. 那么攻击者可以以中间人的方式劫持 http 请求. 来进行后续的攻击. 比如窃听数据. 篡改请求或响应, 跳转到钓鱼网站等操作. 因此 http 请求是不够安全的, 所以最近几年所有的网站都要以 https 来访问的.
那么以劫持 http 请求并跳转到钓鱼网站类为列子, 来看看大致的劫持流程是如下这个样子的.
操作步骤如下:
1. 浏览器会发起一次 http 请求 (比如 http://xxx.abc.com). 发出请求后, 攻击者会以中间人的身份来劫持该 http 请求.
2. 攻击者劫持该 http 请求后, 会把当前请求转发给钓鱼网站 (比如 http://xxx.yyy.com).
3. 钓鱼网站会返回假冒的网页内容.
4. 最后攻击者把假冒的网页内容返回给浏览器.
如上 http 请求根本就没有重定向到 https 网站到, 而是攻击者直接劫持了 http 请求, 最终把钓鱼网站返回给浏览器了. 因此如果直接 http 重定向的话, 会存在一次 http 请求明文的问题, 因此直接使用 http 重定向是不安全的, 因此就出现了 HSTS 来解决这个问题. 下面我们来认识下 HSTS 吧.
2. 认识下 HSTS
如上使用重定向的方式, 把 http 重定向到 https 存在安全性问题, 因为在重定向 https 之前会存在一次 http 明文的请求, 那么攻击者很容易劫持 http 请求, 因此现在我们想当用户浏览器发起 http 请求的时候, 浏览器直接转换成 https 请求. 然后通过 https 请求页面, 这样的话, 攻击者就一般很难进行攻击了. 我们可以请看如下示意图, 如下所示:
步骤可以理解为如下:
1. 用户在浏览器输入 http://xxx.abc.com 的时候, 浏览器知道该域名需要使用 https 来进行通信.
2. 因此浏览器直接向网站发起 https 请求 (比如 https://xxx.abc.com) 这样的.
3. 网站返回响应的内容.
那么现在的问题就是说, 浏览器怎么知道该域名需要使用 https 呢? 因此这个时候我们出现了 HSTS 了.
HSTS 是啥?
HSTS 的全称是 HTTP Strict-Transport-Security. 它是国际互联网工程组织 IETF 发布的一种互联网安全策略机制. 采用 HSTS 策略的网站将保证浏览器始终链接到该网站的 https 加密版本. 不需要用户手动在 URI 地址栏中输入加密地址, 来减少会话被劫持的风险.
HSTS 的基本语法如下:
Strict-Transport-Security: max-age=expireTime [; includeSubDomains] [; preload]
max-age 是必须的参数, 它是一个以秒为单位的数值, 它代表着 HSTS Header 的过期时间, 一般设置为 1 年, 即 31536000 秒.
includeSubDomains 是可选参数, 如果设置该参数的话, 那么意味着当前域名及其子域名均开启 HSTS 的保护.
preload 是可选参数, 只有当你申请将自己的域名加入到浏览器内置列表的时候才需要使用到它.
下面我们先来看下百度的也是这样处理的, 我们先在浏览器 URI 输入 http://www.baidu.com/ 后回车, 浏览器会自动转化成 https://www.baidu.com/ 这样的请求了, 但是我们使用 Chrome 浏览器看网络下的请求可以看到如下会发送 2 次请求, 如下所示:
第二次是 https 请求, 如下所示:
我们可以看到如上, 第一次请求状态码是 307, 并且请求头有这样的标识 "Provisional headers are shown", 具体的含义可以理解为浏览器拦截了该请求, 并且该请求并没有发送出去. 因此浏览器发现该域名需要使用 https 来请求, 所以就发了第二次 https 请求了.
nginx 下配置 HSTS
在 nginx 配置文件上设置 HSTS 响应头部, 代码如下:
add_header Strict-Transport-Security "max-age=172800; includeSubDomains"
因此 nginx 的配置如下:
- server {
- listen xxx.abc.com;
- server_name xxx.abc.com;
- rewrite ^/(.*)$ https://$host$1 permanent;
- }
- server {
- listen 443 ssl;
- server_name xxx.abc.com;
- add_header Strict-Transport-Security "max-age=172800; includeSubDomains";
- ssl_certificate cert/server.crt;
- ssl_certificate_key cert/server.key;
- ssl_session_cache shared:SSL:1m;
- ssl_session_timeout 5m;
- ssl_ciphers HIGH:!aNULL:!MD5;
- ssl_prefer_server_ciphers on;
- location / {
- proxy_pass http://localhost:3001;
- }
- }
然后 nginx 配置保存, 然后重启.
当我重启后, 第一次使用 https 方式访问我的网站, nginx 会告诉客户端浏览器, 以后如果用户输入的是 http, 也要让浏览器以 https 来访问我的 nginx 服务器, 如下所示:
但是如果 nginx 重启后, 第一次使用 http 访问的话, 虽然跳转了, 但是并没有使用 HSTS 了, 因为要跳转到 https, 才会使用 HSTS. 但是当我再输入 http 了就会有 307 状态码, 并且有 "Provisional headers are shown" 这样的提示.
理解 HSTS Preload List
HSTS 虽然可以解决 HTTPS 的降级攻击, 但是对于 HSTS 生效前首次的 http 请求, 依然是无法避免 http 请求被劫持的问题, 比如我们第一次浏览器清除缓存, 然后第一次使用 http 请求的话, 第一次 http 也是明文传输的, 当跳转到 https 后会使用 HSTS 的, 以后只要浏览器缓存不清除的话, nginx 不重启的话, 都会使用 HSTS 保护的. 因此为了解决第一次 http 请求的问题, 浏览器厂商们为了解决这个问题, 提出了 HSTS Preload List 的方案, 内置一份可以定期更新的表, 对于列表中的域名, 即使用户之前没有访问过, 也会使用 https 协议请求的.
目前这个 Preload List 由 Google Chrome 维护, Chrome,Firefox,Safari,IE 11 和 Microsoft Edge 都在使用. 如果要想把自己的域名加进这个列表, 首先需要满足以下条件:
1. 拥有合法的证书 (如果使用 SHA-1 证书, 过期时间必须早于 2016 年);
2. 将所有 HTTP 流量重定向到 HTTPS;
3. 确保所有子域名都启用了 HTTPS;
4. 输出 HSTS 响应头:
5. max-age 不能低于 18 周 (10886400 秒);
6. 必须指定 includeSubdomains 参数;
7. 必须指定 preload 参数;
即便满足了上述所有条件, 也不一定能进入 HSTS Preload List, 更多信息可以查看: https://hstspreload.org/.
通过 Chrome 的 Chrome://net-internals/#hsts 工具, 可以查询某个网站是否在 PreloadList 之中, 还可以手动把某个域名加到本机 Preload List.
HSTS 缺点
HSTS 并不是 HTTP 会话劫持的完美解决方案. 用户首次访问某网站是不受 HSTS 保护的. 这是因为首次访问时, 浏览器还未收到 HSTS, 所以仍有可能通过明文 HTTP 来访问.
如果用户通过 HTTP 访问 HSTS 保护的网站时, 以下几种情况存在降级劫持可能:
1. 以前从未访问过该网站.
2. 最近重新安装了其操作系统.
3. 最近重新安装了其浏览器.
4. 切换到新的浏览器.
5. 删除浏览器的缓存.
6. 最近没访问过该站并且 max-age 过期了.
那么解决该问题的方法, 可以使用上面介绍的 HSTS Preload List 方法.
支持 HSTS 浏览器
目前主流浏览器都已经支持 HSTS 特性, 具体可参考下面列表:
Google Chrome 4 及以上版本
Firefox 4 及以上版本
Opera 12 及以上版本
Safari 从 OS X Mavericks 起
Internet Explorer 及以上版本
来源: https://www.cnblogs.com/tugenhua0707/p/10945934.html