如何比较安全的配置 Web 服务器之一 - TLS 和 HTTPS
又是一次知道的已经做好了,不知道的对着他耳朵唠叨也不会去做的系列节目。(嗯?) 如果不想听咱啰嗦的话, 直接去看 Mozilla Infosec 的相同主题的内容就好啦。
HTTPS 和 TLS 到底是什么关系?
HTTPS 其实就是包在 SSL/TLS 里的 HTTP 啦,考虑到 SSL 3.0 早在 2015 年就废弃了,现在基本上就只提 TLS 了。(虽然 TLS 1.0 和 1.1 也在 2020 年被废弃了就是。)
因为 HTTPS 能够加密所交换的数据、在传输的过程中保证数据的完整性,以及证明用户是在与目标网站进行通信。所以很多组织都在呼吁让整个互联网的流量都是加密的。 以及各大浏览器也在逐渐的将只支持 HTTP 的网站列入“不安全”的范围。(虽然也有人对这么做有没有作用存疑。)
取得一份证书
关于 TLS 的实现原理不是咱的重点(其实是咱也不是很懂,汝也许可以看看 IETF 的 RFC, 如果真的有必要的话。 汝现在需要知道的最重要的一点就是,要使用 HTTPS 的话,网站管理员要先从大家都信任的某些地方(就是 CA/数字证书认证机构 啦)取得一份带有私钥的证书才能继续接下来的步骤。
有不止一家企业和组织营运的 CA 负责签发证书,例如历史悠久的 Entrust 和 DigiCert ,和数个公司或组织为了普及 HTTPS 创办的 Let's Encrypt 等等。
如果汝还没有预算购买证书的话,可以先从 Let's Encrypt 开始。
不过 Let's Encrypt 的证书有效期只有三个月,所以如果汝对自己的服务没有完整的命令行访问权限(例如比较常见的虚拟共享主机)的话,可能会稍微麻烦一些。
至于怎么申请证书嘛, Let's Encrypt 推荐使用它们制作的 certbot 帮助程序。 可以在这里查阅文档,咱就先跳过了(笑)
如果希望只允许特定的 CA 给汝的网站签发证书的话,可以通过 DNS 证书颁发机构授 权记录(简称 CAA)来实现,CA 会提供相应的指南告诉汝如何添加和修改这种记录。
规划网站的兼容目标
越新的标准越能提升安全性,越旧的浏览器就越有可能不支持,大概就是这个样子。所以根据自己网站的受众决定向后兼容的程度就很重要。Mozilla 的标准有三个等级:
- 现代等级:只支持 TLS 1.3,适用于不需要向后兼容的新网站。
- 中等等级:支持 TLS 1.2 和 1.3,适用于大多数网站和估计近五年间的设备。
- 向后兼容等级:只有汝需要考虑支持像是 Windows XP 或者 Java 6 这种相当老的系统时才应该使用这个等级。
Mozilla 也提供一个线上配置生成器,可以根据不同的等级和服务器软件生成合适的参考配置文件。 根据汝的需要稍加修改(例如证书的位置)应该就可以使用了。
正确的重定向 HTTP 链接
现在汝的网站有 HTTPS 版本了,很好。但只有用户主动的输入带有 https://
的地址的时候才能访问 HTTPS 版本, 于是汝还需要让用户通过 HTTP 访问的时候能自动重定向到 HTTPS 版本。
对于目前比较常见的 Apache 和 Nginx 来说,都挺简单的。
# Nginx 示例配置。 server { listen 80; return 301 https://$host$request_uri; } # Apache 的示例配置,记得把 foo.tld 换成汝自己的域名。 <VirtualHost *:80>ServerName foo.tld Redirect permanent / https://foo.tld/ </VirtualHost>
HTTP 严格传输安全 (HSTS)
汝设置好了正确的重定向,这本来没有问题,直到攻击者劫持了访问汝的网站第一步的 HTTP 响应…… 一场典型的 SSL 剥离攻击开始了。
所以 IETF 为了补救这个缺陷提出了 HTTP 严格传输安全标准,汝也许经常能看到它的缩写 HSTS 。
要实现 HTTP 严格传输安全,核心就是一个 HTTP 首部,例如下面的这个:
Strict-Transport-Security: max-age=31536000; includeSubDomains
如果 https://example.com
的响应中有这段的话:
- 在接下来的 31536000 秒(即一年)中,浏览器向 example.com 发送 HTTP 请求时,必须采用 HTTPS 来发起连接。 比如,用户点击超链接或在地址栏输入 http://example.com/ ,浏览器应当自动将 http 转写成 https, 然后直接向 https://example.com/ 发送请求。
- 在接下来的一年中,如果 example.com 服务器发送的TLS证书无效,用户不能忽略浏览器警告继续访问网站。
- 这也适用于 example.com 下面的子域名,例如 www.example.com 。
以及这段只有在 HTTPS 响应中才会生效,那汝应该也会想那么第一次访问网站时怎么办。 各大浏览器的做法是维护一个预先加载列表,在列表上的网站总是会通过 HTTPS 访问。 汝可以参考这个网站来了解将自己的网站加入预先加载列表的要求。
OCSP 装订
汝也许听说过一个叫 OCSP(在线证书状态协议)的东西。浏览器每次连接支持 OCSP 的网站时,都会向 CA 发送请求查询这个证书的状态。 但这就带来了两个问题:
- OCSP 是明文发送的,汝和 CA 之间的任何人都能知道汝在访问哪个网站,虽然不能精确到 URL,但进行阻断的话已经足够了。
- 如果 CA 提供 OCSP 查询的服务器偷偷的留下了访问的记录……
而 OCSP 装订则改为了服务器向 CA 获取 OCSP 响应并缓存一段时间,用户访问时只要验证从服务器发送来的 OCSP 响应的有效性即可。
# Nginx 示例配置。 server { ... # OCSP staplingssl_stapling on; ssl_stapling_verify on; # 如果汝的 CA 没有提供 OCSP 使用的证书(例如 Let's Encrypt 就没有), # 那么不需要设置 ssl_trusted_certificate 属性。 ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates; # 设置成汝服务器使用的 DNS 服务器。 resolver 127.0.0.1; } # Apache 的示例配置。 # OCSP 装订缓存的位置, SSLStaplingCache shmcb:/tmp/stapling_cache(128000) <VirtualHost *:443> ... # 如果汝的 CA 没有提供 OCSP 使用的证书(例如 Let's Encrypt 就没有), # 那么不需要设置 SSLCertificateChainFile 属性。 SSLCertificateChainFile /path/to/DigiCertCA.crt SSLUseStapling on </VirtualHost>
Referrer 策略
Referrer 经常用来表示用户是从哪里来的,也有人担心它会泄露一些隐私信息。 Referrer-Policy 首部用来监管哪些访问来源信息——会在 Referer 中发送——应该被包含在生成的请求当中。
汝可以根据汝的网站和用户选择适合的模式。
# no-referrer-when-downgrade 是现代浏览器的默认设置, # 在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。 Referrer-Policy: no-referrer-when-downgrade # 只有原地址相同时才发送 Referrer。 Referrer-Policy: same-origin # 对于同源的请求,会发送完整的URL作为引用地址; # 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。 Referrer-Policy: strict-origin-when-cross-origin # 对不支持 strict-origin-when-cross-origin 的浏览器不发送首部。 Referrer-Policy: no-referrer, strict-origin-when-cross-origin # 或者直接什么时候都不发送首部。 Referrer-Policy: no-referrer
X-Content-Type-Options 、 X-Frame-Options 和 X-XSS-Protection
X-Content-Type-Options 用来被服务器用来提示客户端一定要遵循在 Content-Type 首部中对 MIME 类型 的设定, 而不能对其进行修改。这就禁用了客户端的 MIME 类型嗅探行为。
# 防止浏览器不正确的识别文件类型,例如把非脚本文件识别成脚本。 X-Content-Type-Options: nosniff
The X-Frame-Options HTTP 响应头是用来给浏览器指示一个页面可否在框架中展现的标记。 (其实还有 <frame>
, <embed>
和 <object>
的,但这些好像都被弃用了来着。)
站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免被用来进行汝点击的不是实际的地方的那种 clickjacking 攻击。 除了 HTTP 首部以外,内容安全策略(CSP)里的 frame-ancestors
属性也能控制这种行为,但是那个好复杂啊……
# 阻止网页嵌入在框架中。 # 第一行是通过 CSP 设置,第二行是传统的 HTTP 首部形式,它们的效果是相同的。 Content-Security-Policy: frame-ancestors 'none' X-Frame-Options: DENY # 只允许相同域名的网页嵌入在框架中。 Content-Security-Policy: frame-ancestors 'self' X-Frame-Options: SAMEORIGIN # 只允许特定来源的网页嵌入在框架中。 # Content-Security-Policy: frame-ancestors https://framer.mozilla.org X-Frame-Options: DENY
X-XSS-Protection 响应头是 Internet Explorer,Chrome 和 Safari 的一个特性, 当检测到跨站脚本攻击时,浏览器将停止加载页面。
若网站设置了良好的 Content-Security-Policy 来禁用内联 JavaScript ('unsafe-inline'), 现代浏览器不太需要这些保护, 但其仍然可以为尚不支持 CSP 的旧版浏览器的用户提供保护。
# 启用 X-XSS-Protection,如果 mode 为 block 的话, # 浏览器将在检测到可能的跨站脚本攻击时停止加载而不是清空页面。 X-XSS-Protection: 1; mode=block
至于那个最复杂的 CSP 嘛…… 下次再说了……
更多资源
除了上面提到的 Mozilla 的文档和配置生成器以外:
- Qualys SSLlab 可以帮汝测试汝的 HTTPS 配置, 也提供了一些可以参考的文档。
- BadSSL可以测试汝正在使用的浏览器遇到错误的 TLS 设置时的反应。