关于启用 HTTPS 的一些经验分享(二)

2015/12/24 · 基础技术 ·
HTTP,
HTTPS

原文出处:
imququ(@屈光宇)   

文章目录

  • SSL 版本选择
  • 加密套件选择
  • SNI 扩展
  • 证书选择

几天前,一位朋友问我:都说推荐用 Qualys SSL
Labs 这个工具测试 SSL
安全性,为什么有些安全实力很强的大厂家评分也很低?我认为这个问题应该从两方面来看:1)国内用户终端情况复杂,很多时候降低
SSL 安全配置是为了兼容更多用户;2)确实有一些大厂家的 SSL
配置很不专业,尤其是配置了一些明显不该使用的 CipherSuite。

我之前写的《关于启用 HTTPS
的一些经验分享(一)》,主要介绍 HTTPS
如何与一些新出的安全规范配合使用,面向的是现代浏览器。而今天这篇文章,更多的是介绍启用
HTTPS 过程中在老旧浏览器下可能遇到的问题,以及如何取舍。

关于启用 HTTPS 的一些经验分享

2015/12/04 · 基础技术 ·
HTTP,
HTTPS

原文出处:
imququ(@屈光宇)   

随着国内网络环境的持续恶化,各种篡改和劫持层出不穷,越来越多的网站选择了全站
HTTPS。就在今天,免费提供证书服务的 Let’s
Encrypt 项目也正式开放,HTTPS 很快就会成为
WEB 必选项。HTTPS 通过 TLS
层和证书机制提供了内容加密、身份认证和数据完整性三大功能,可以有效防止数据被查看或篡改,以及防止中间人冒充。本文分享一些启用
HTTPS 过程中的经验,重点是如何与一些新出的安全规范配合使用。至于 HTTPS
的部署及优化,之前写过很多,本文不重复了。

背景

最近为了扛 DDoS
攻击,从移动公司申请了一台服务器,移动公司免费提供流量清洗功能。但由于没有备案,移动公司不允许开通
80 端口。

这是一份昨天在开发者头条上最受大家欢迎的优质文章列表,头条君每日清晨为您送达,不见不散!

其实由于GitLab只负责监听本地socket文件,而web服务器采用了Nginx等。只需要在web
server上做适当的配置即可。

SSL 版本选择

TLS(Transport Layer Security,传输层安全)的前身是 SSL(Secure Sockets
Layer,安全套接字层),它最初的几个版本(SSL 1.0、SSL 2.0、SSL
3.0)由网景公司开发,从 3.1 开始被 IETF 标准化并改名,发展至今已经有 TLS
1.0、TLS 1.1、TLS 1.2 三个版本。TLS 1.3 改动会比较大,目前还在草案阶段。

SSL 1.0 从未公开过,而 SSL 2.0 和 SSL 3.0
都存在安全问题,不推荐使用。Nginx 从 1.9.1 开始默认只支持 TLS
的三个版本,以下是 Nginx
官方文档中对
ssl_protocols 配置的说明:

Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1]
[TLSv1.2];
Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server
Enables the specified protocols. The TLSv1.1 and TLSv1.2 parameters
work only when the OpenSSL library of version 1.0.1 or higher is used.

但不幸的是,IE 6 只支持 SSLv2 和
SSLv3(来源),也就是说
HTTPS 网站要支持 IE 6,就必须启用 SSLv3。仅这一项就会导致 SSL Labs
给出的评分降为 C。

理解 Mixed Content

HTTPS 网页中加载的 HTTP 资源被称之为 Mixed
Content(混合内容),不同浏览器对 Mixed Content 有不一样的处理规则。

目标

为了尽快启用这台担负着负载均衡和反向代理(其实没有负载均衡)的服务器,我计划使用
https 协议,利用 443 端口从而避开 80
端口为客户提供正常服务。同时通过使用 https 协议,使得网站访问更加安全。

昨日最佳 Top 3:

下面是一个采用Nginx的例子,对GitLab安装指南下载的gitlab脚本文件做了适当的修改。

加密套件选择

加密套件(CipherSuite),是在 SSL
握手中需要协商的很重要的一个参数。客户端会在 Client Hello
中带上它所支持的 CipherSuite 列表,服务端会从中选定一个并通过
Server Hello 返回。如果客户端支持的 CipherSuite 列表与服务端配置的
CipherSuite 列表没有交集,会导致无法完成协商,握手失败。

CipherSuite
包含多种技术,例如认证算法(Authentication)、加密算法(Encryption)、消息认证码算法(Message
Authentication Code,简称为 MAC)、密钥交换算法(Key
Exchange)和密钥衍生算法(Key Derivation Function)。

SSL 的 CipherSuite 协商机制具有良好的扩展性,每个 CipherSuite 都需要在
IANA 注册,并被分配两个字节的标志。全部 CipherSuite 可以在 IANA 的 TLS
Cipher Suite
Registry
页面查看。

OpenSSL 库支持的全部 CipherSuite 可以通过以下命令查看:

openssl ciphers -V | column -t 0xCC,0x14 – ECDHE-ECDSA-CHACHA20-POLY1305
TLSv1.2 Kx=ECDH Au=ECDSA Enc=ChaCha20-Poly1305 Mac=AEAD … …

1
2
3
openssl ciphers -V | column -t
0xCC,0x14  –  ECDHE-ECDSA-CHACHA20-POLY1305  TLSv1.2  Kx=ECDH        Au=ECDSA   Enc=ChaCha20-Poly1305  Mac=AEAD
… …

0xCC,0x14 是 CipherSuite 的编号,在 SSL
握手中会用到。ECDHE-ECDSA-CHACHA20-POLY1305
是它的名称,之后几部分分别表示:用于 TLSv1.2,使用 ECDH 做密钥交换,使用
ECDSA 做认证,使用 ChaCha20-Poly1305 做对称加密,由于 ChaCha20-Poly1305
是一种 AEAD 模式,不需要 MAC 算法,所以 MAC 列显示为 AEAD。

要了解 CipherSuite 的更多内容,可以阅读这篇长文《TLS 协议分析 与
现代加密通信协议设计》。总之,在配置
CipherSuite 时,请务必参考权威文档,如:Mozilla
的推荐配置、CloudFlare
使用的配置。

以上 Mozilla 文档中的「Old backward compatibility」配置,以及 CloudFlare
的配置,都可以很好的兼容老旧浏览器,包括 Windows XP / IE6。

之前见到某个大厂家居然支持包含 EXPORT
CipherSuite,这些套件在上世纪由于美国出口限制而被弱化过,已被攻破,实在没有理由再使用。

早期的 IE

早期的 IE 在发现 Mixed Content
请求时,会弹出「是否只查看安全传送的网页内容?」这样一个模态对话框,一旦用户选择「是」,所有
Mixed Content 资源都不会加载;选择「否」,所有资源都加载。

步骤

1.如何免费地让网站启用
HTTPS

# GITLAB
# Maintainer: @randx
# App Version: 4.0

SNI 扩展

我们知道,在 Nginx 中可以通过指定不同的 server_name
来配置多个站点。HTTP/1.1 协议请求头中的 Host
字段可以标识出当前请求属于哪个站点。但是对于 HTTPS 网站来说,要想发送
HTTP 数据,必须等待 SSL
握手完成,而在握手阶段服务端就必须提供网站证书。对于在同一个 IP 部署不同
HTTPS 站点,并且还使用了不同证书的情况下,服务端怎么知道该发送哪个证书?

Server Name Indication,简称为 SNI,是 TLS
的一个扩展,为解决这个问题应运而生。有了 SNI,服务端可以通过
Client Hello 中的 SNI 扩展拿到用户要访问网站的 Server
Name,进而发送与之匹配的证书,顺利完成 SSL 握手。

Nginx 在很早之前就支持了 SNI,可以通过 nginx -V
来验证。以下是我的验证结果:

./nginx -V nginx version: nginx/1.9.9 built by gcc 4.8.4 (Ubuntu
4.8.4-2ubuntu1~14.04) built with OpenSSL 1.0.2e-dev xx XXX xxxx TLS SNI
support enabled configure arguments: –with-openssl=../openssl
–with-http_ssl_module –with-http_v2_module

1
2
3
4
5
6
./nginx -V
nginx version: nginx/1.9.9
built by gcc 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
built with OpenSSL 1.0.2e-dev xx XXX xxxx
TLS SNI support enabled
configure arguments: –with-openssl=../openssl –with-http_ssl_module –with-http_v2_module

然而,并不是所有浏览器都支持 SNI,以下是常见浏览器支持 SNI 的最低版本:

浏览器 最低版本
Chrome Vista+ 全支持;XP 需要 Chrome 6+;OSX 10.5.7+ 且 Chrome 5+
Firefox 2.0+
Internet Explorer 7+ (需要 Vista+)
Safari 3+ (需要 OS X 10.5.6+)
Mobile Safari iOS 4.0+
Android Webview 3.0+

如果要避免在不支持 SNI 的浏览器中出现证书错误,只能将使用不同证书的
HTTPS 站点部署在不同 IP 上,最简单的做法是分开部署到不同机器上。

比较新的 IE

比较新的 IE
将模态对话框改为页面底部的提示条,没有之前那么干扰用户。而且默认会加载图片类
Mixed Content,其它如 JavaScript、CSS
等资源还是会根据用户选择来决定是否加载。

选择证书颁发机构

有很多证书颁发机构,以前也使用过 StartSSL
的证书,但后来被中国的企业收购了,因此直接排除。当前免费又火的证书颁发机构当属
Let’s encrypt 了,因此本次我也采用该机构的证书。

2.有赞风控规则引擎实践

upstream gitlab {
  server unix:/home/gitlab/gitlab/tmp/sockets/gitlab.socket;
}

证书选择

HTTPS 网站需要通过 CA
取得合法证书,证书通过数字签名技术确保第三方无法伪造。证书的简单原理如下:

  • 根据版本号、序列号、签名算法标识、发行者名称、有效期、证书主体名、证书主体公钥信息、发行商唯一标识、主体唯一标识、扩展生成
    TBSCertificate(To Be Signed Certificate, 待签名证书)信息;
  • 签发数字签名:使用 HASH 函数对 TBSCertificate 计算得到消息摘要,用
    CA 的私钥对消息摘要进行加密,得到签名;
  • 校验数字签名:使用相同的 HASH 函数对 TBSCertificate
    计算得到消息摘要,与使用 CA 公钥解密签名得到内容相比较;

使用 SHA-1 做为 HASH 函数的证书被称之为 SHA-1 证书,由于目前已经找到
SHA-1 的碰撞条件,将证书换成使用更安全的 SHA-2 做为 HASH 函数的 SHA-2
证书被提上日程。

实际上,微软已经宣称自 2017 年 1 月 1 日起,将全面停止对 SHA-1
证书的支持。届时在最新版本的 Windows 系统中,SHA-1 证书将不被信任。

而根据 Chrome
官方博客的文章,使用
SHA-1 证书且证书有效期在 2016 年 1 月 1 号至 2016 年 12 月 31
号之间的站点会被给予「安全的,但存在漏洞」的提示,也就是地址栏的小锁不再是绿色的,并且会有一个黄色小三角。而使用
SHA-1 证书且证书有效期超过 2017 年 1 月 1
号的站点会被给予「不安全」的红色警告,小锁上直接显示一个红色的叉。

然而,并不是所有的终端都支持 SHA-2
证书,服务端不支持还好办,浏览器只能依赖于用户升级了。下面是常见浏览器支持
SHA-2 证书的最低版本:

浏览器 支持 SHA-2 证书的最低版本
Chrome 26+
Firefox 1.5+
Internet Explorer 6+ (需要 XP SP3+)
Safari 3+ (需要 OS X 10.5+)
Android Webview 2.3+

可以看到,如果要照顾没有打 XP SP3 补丁的 IE6 用户,只能继续使用 SHA-1
证书。

在我之前的文章中,还提到过 ECC
证书,这种新型的证书支持度更差,这里略过不提,有兴趣的同学可以点这里查看。

是否可以针对不同浏览器启用不同证书呢?理论上服务端可以根据客户端
Client Hello 中的 Cipher Suites 特征以及是否支持 SNI
的特征来分配不同证书,不过我没有实际验证过。

本文先写这么多,很多策略都需要根据自己网站的用户来决定,例如我的博客基本没有
IE8- 用户,理所当然可以禁用
SSLv3。如果你的产品还有很多使用老旧浏览器的用户,那就必须为这些用户做兼容方案了。一种方案是:只把主域安全级别配低,将
XP 上 IE 用户的 HTTPS 请求直接重定向到 HTTP
版本,这样其它域名可以使用高安全级别的配置,运维起来比较方便。

1 赞 1 收藏
评论

图片 1

现代浏览器

现代浏览器(Chrome、Firefox、Safari、Microsoft Edge),基本上都遵守了
W3C 的 Mixed Content 规范,将
Mixed Content 分为Optionally-blockable 和 Blockable 两类:

Optionally-blockable 类 Mixed Content
包含那些危险较小,即使被中间人篡改也无大碍的资源。现代浏览器默认会加载这类资源,同时会在控制台打印警告信息。这类资源包括:

  • 通过 <img> 标签加载的图片(包括 SVG 图片);
  • 通过 <video> / <audio> 和 <source> 标签加载的视频或音频;
  • 预读的(Prefetched)资源;

除此之外所有的 Mixed Content
都是 Blockable,浏览器必须禁止加载这类资源。所以现代浏览器中,对于
HTTPS 页面中的 JavaScript、CSS 等 HTTP
资源,一律不加载,直接在控制台打印错误信息。

设置正常的访问规则和安全规则

  1. 设置应用服务器;
  2. 设置反向代理服务器;
  3. 设置域名解析;

3.[译]
JavaScript 工作机制:V8 引擎内部机制及如何编写优化代码的 5
个诀窍

server {
  listen 443;
  ssl                  on;
  ssl_certificate      /etc/nginx/sites-available/server.crt;
  ssl_certificate_key  /etc/nginx/sites-available/server.key;

移动浏览器

前面所说都是桌面浏览器的行为,移动端情况比较复杂,当前大部分移动浏览器默认都允许加载
Mixed Content。也就是说,对于移动浏览器来说,HTTPS 中的 HTTP
资源,无论是图片还是 JavaScript、CSS,默认都会加载。

一般选择了全站 HTTPS,就要避免出现 Mixed Content,页面所有资源请求都走
HTTPS 协议才能保证所有平台所有浏览器下都没有问题。

设置应用服务器

  • 将所有应用都配置好;
  • 通过 PM2 统一的配置文件运行所有应用;
  • 应用服务器防火墙开放 8000 ~ 8999 区间的端口;
  • 设置安全组。入方向只允许所有 IP 地址访问 22 的 TCP
    端口,以及反向代理服务器的 IP 地址访问 8000 ~ 8999 的 TCP
    端口;出方向允许所有端口和协议;

50 万程序员都在用的 App,扫描下方二维码,即刻体验!

  server_name localhost;
  #Ubuntu1204-dell
source.cml.com;    # e.g., server_name source.example.com;
  root /home/gitlab/gitlab/public;

合理使用 CSP

CSP,全称是 Content Security
Policy,它有非常多的指令,用来实现各种各样与页面内容安全相关的功能。这里只介绍两个与
HTTPS 相关的指令,更多内容可以看我之前写的《Content Security Policy
Level 2
介绍》。

设置反向代理服务器

  • 在反向代理服务器上配置所有应用服务器应用的域名解析;

图片 2

 

block-all-mixed-content

前面说过,对于 HTTPS 中的图片等 Optionally-blockable 类 HTTP
资源,现代浏览器默认会加载。图片类资源被劫持,通常不会有太大的问题,但也有一些风险,例如很多网页按钮是用图片实现的,中间人把这些图片改掉,也会干扰用户使用。

通过 CSP
的 block-all-mixed-content 指令,可以让页面进入对混合内容的严格检测(Strict
Mixed Content Checking)模式。在这种模式下,所有非 HTTPS
资源都不允许加载。跟其它所有 CSP
规则一样,可以通过以下两种方式启用这个指令:

HTTP 响应头方式:

JavaScript

Content-Security-Policy: block-all-mixed-content

1
Content-Security-Policy: block-all-mixed-content

<meta> 标签方式:

XHTML

<meta http-equiv=”Content-Security-Policy”
content=”block-all-mixed-content”>

1
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">

设置域名解析

  • 将所有域名解析都指向反向代理服务器的 IP 地址;

50 万程序员都在用的 App

  # individual nginx logs for this gitlab vhost
  access_log  /var/log/nginx/gitlab_access.log;
  error_log  /var/log/nginx/gitlab_error.log;

upgrade-insecure-requests

历史悠久的大站在往 HTTPS
迁移的过程中,工作量往往非常巨大,尤其是将所有资源都替换为 HTTPS
这一步,很容易产生疏漏。即使所有代码都确认没有问题,很可能某些从数据库读取的字段中还存在
HTTP 链接。

而通过 upgrade-insecure-requests 这个 CSP
指令,可以让浏览器帮忙做这个转换。启用这个策略后,有两个变化:

  • 页面所有 HTTP 资源,会被替换为 HTTPS 地址再发起请求;
  • 页面所有站内链接,点击后会被替换为 HTTPS 地址再跳转;

跟其它所有 CSP
规则一样,这个指令也有两种方式来启用,具体格式请参考上一节。需要注意的是 upgrade-insecure-requests 只替换协议部分,所以只适用于
HTTP/HTTPS 域名和路径完全一致的场景。

配置 https 协议

  location / {
    # serve static files from defined root folder;.
    # @gitlab is a named location for the upstream fallback, see
below
    try_files $uri $uri/index.html $uri.html @gitlab;
  }

合理使用 HSTS

在网站全站 HTTPS 后,如果用户手动敲入网站的 HTTP
地址,或者从其它地方点击了网站的 HTTP 链接,依赖于服务端 301/302
跳转才能使用 HTTPS 服务。而第一次的 HTTP
请求就有可能被劫持,导致请求无法到达服务器,从而构成 HTTPS 降级劫持。

参考资料

  # if a file, which is not found in the root folder is requested,
  # then the proxy pass the request to the upsteam (gitlab unicorn)
  location @gitlab {
    proxy_read_timeout 300; #

    proxy_connect_timeout 300; #

    proxy_redirect    off;

HSTS 基本使用

这个问题可以通过 HSTS(HTTP Strict Transport
Security,RFC6797)来解决。HSTS
是一个响应头,格式如下:

JavaScript

Strict-Transport-Security: max-age=expireTime [; includeSubDomains]
[; preload]

1
Strict-Transport-Security: max-age=expireTime [; includeSubDomains] [; preload]

max-age,单位是秒,用来告诉浏览器在指定时间内,这个网站必须通过 HTTPS
协议来访问。也就是对于这个网站的 HTTP 地址,浏览器需要先在本地替换为
HTTPS 之后再发送请求。

includeSubDomains,可选参数,如果指定这个参数,表明这个网站所有子域名也必须通过
HTTPS 协议来访问。

preload,可选参数,后面再介绍它的作用。

HSTS 这个响应头只能用于 HTTPS 响应;网站必须使用默认的 443
端口;必须使用域名,不能是 IP。而且启用 HSTS
之后,一旦网站证书错误,用户无法选择忽略。

    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_set_header  Host              $http_host;
    proxy_set_header  X-Real-IP        $remote_addr;

HSTS Preload List

可以看到 HSTS 可以很好的解决 HTTPS 降级攻击,但是对于 HSTS 生效前的首次
HTTP 请求,依然无法避免被劫持。浏览器厂商们为了解决这个问题,提出了 HSTS
Preload List
方案:内置一份列表,对于列表中的域名,即使用户之前没有访问过,也会使用
HTTPS 协议;列表可以定期更新。

目前这个 Preload List 由 Google Chrome 维护,Chrome、Firefox、Safari、IE
11 和 Microsoft Edge
都在使用。如果要想把自己的域名加进这个列表,首先需要满足以下条件:

  • 拥有合法的证书(如果使用 SHA-1 证书,过期时间必须早于 2016 年);
  • 将所有 HTTP 流量重定向到 HTTPS;
  • 确保所有子域名都启用了 HTTPS;
  • 输出 HSTS 响应头:
    • max-age 不能低于 18 周(10886400 秒);
    • 必须指定 includeSubdomains 参数;
    • 必须指定 preload 参数;

即便满足了上述所有条件,也不一定能进入 HSTS Preload
List,更多信息可以看这里。通过
Chrome 的 chrome://net-internals/#hsts工具,可以查询某个网站是否在
Preload List 之中,还可以手动把某个域名加到本机 Preload List。

对于 HSTS 以及 HSTS Preload List,我的建议是只要你不能确保永远提供 HTTPS
服务,就不要启用。因为一旦 HSTS 生效,你再想把网站重定向为
HTTP,之前的老用户会被无限重定向,唯一的办法是换新域名。

    proxy_pass ;
  }
}

CDN 安全

对于大站来说,全站迁移到 HTTPS 后还是得用 CDN,只是必须选择支持 HTTPS 的
CDN 了。如果使用第三方 CDN,安全方面有一些需要考虑的地方。

注意 server { 下面的四行。

合理使用 SRI

HTTPS
可以防止数据在传输中被篡改,合法的证书也可以起到验证服务器身份的作用,但是如果
CDN 服务器被入侵,导致静态文件在服务器上被篡改,HTTPS 也无能为力。

W3C 的 SRI(Subresource
Integrity)规范可以用来解决这个问题。SRI
通过在页面引用资源时指定资源的摘要签名,来实现让浏览器验证资源是否被篡改的目的。只要页面不被篡改,SRI
策略就是可靠的。

有关 SRI 的更多说明请看我之前写的《Subresource Integrity
介绍》。SRI 并不是
HTTPS
专用,但如果主页面被劫持,攻击者可以轻松去掉资源摘要,从而失去浏览器的
SRI 校验机制。

监听443端口,启用ssl,server.crt和server.key文件是参考Nginx的文档生成的。

了解 Keyless SSL

另外一个问题是,在使用第三方 CDN 的 HTTPS
服务时,如果要使用自己的域名,需要把对应的证书私钥给第三方,这也是一件风险很高的事情。

CloudFlare 公司针对这种场景研发了 Keyless SSL
技术。你可以不把证书私钥给第三方,改为提供一台实时计算的 Key Server
即可。CDN 要用到私钥时,通过加密通道将必要的参数传给 Key Server,由 Key
Server 算出结果并返回即可。整个过程中,私钥都保管在自己的 Key Server
之中,不会暴露给第三方。

CloudFlare
的这套机制已经开源,如需了解详情,可以查看他们官方博客的这篇文章:Keyless
SSL: The Nitty Gritty Technical
Details。

好了,本文先就写到这里,需要注意的是本文提到的 CSP、HSTS 以及 SRI
等策略都只有最新的浏览器才支持,详细的支持度可以去CanIUse 查。切换到
HTTPS
之后,在性能优化上有很多新工作要做,这部分内容我在之前的博客中写过很多,这里不再重复,只说最重要的一点:既然都
HTTPS 了,赶紧上 HTTP/2 才是正道。

1 赞 4 收藏
评论

图片 1

最后proxy_pass 不能修改,不要改成https,否则不能工作。

现在试一下用https的方式check out 代码:

git clone https://….

报错,说证书校验有问题:

error: server certificate verification failed. CAfile:
/etc/ssl/certs/ca-certificates.crt CRLfile: none

最简单的解决方法是加一个环境变量:

export GIT_SSL_NO_VERIFY=1

GitLab
的详细介绍
:请点这里
GitLab
的下载地址
:请点这里

图片 4

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图