关于 HTTPS ,基本上你想知道的都在这里了。本文原标题《HTTPS 原理与实践》,下图是本文配套 PPT 的目录截图:

先说一下,本文可能有些地方由于描述不到位或者我本人理解错误而出现不准确内容,有错误欢迎指正!
HTTPS 全称 Hyper Text Transfer Protocol over Secure Socket Layer ,直译过来就是通过 SSL 实现的超文本传输协议,简单来讲就是加密版的 HTTP 协议,也就是 HTTP+SSL/TLS 。

为什么需要加密版的 HTTP 呢,因为我们都知道,HTTP 是明文传输的,因此使用 HTTP 协议传输隐私信息非常不安全,很容易在传输过程中被窃取,或者通信内容被人篡改冒充,使用 HTTPS 可以避免这些问题。
为了解决 HTTP 明文传输的风险性,网景公司设计了 SSL( Secure Sockets Layer )协议用于对 HTTP 协议传输的数据进行加密,从而就诞生了 HTTPS。 SSL 目前的版本是 3.0,被 IETF( Internet Engineering Task Force )定义在 RFC 6101 中,之后 IETF 对 SSL 3.0 进行了升级,于是出现了 TLS( Transport Layer Security ) 1.0,定义在 RFC 2246。实际上我们现在的 HTTPS 都是用的 TLS 协议,但是由于 SSL 出现的时间比较早,并且依旧被现在浏览器所支持,因此 SSL 依然是 HTTPS 的代名词。
上面一大段话是转载的,简单而言就是 TLS 是 SSL 的升级版,现在浏览器一般用的都是 TLS。
从底层分析 HTTPS 有以下 3 大优点:
从实际出发来看,HTTPS 主要有以下优点:
下面单独对 3 和 4 说一下。
运营商 HTTP 劫持我们都知道,经常莫名奇妙的在自己网站上看到各种恶意的恶心广告,查看代码发现被注入了一些奇奇怪怪的 js,这其实就是 HTTP 劫持,启用了 HTTPS 之后,我们传输的内容都是加密的,运营商想篡改内容都没辙。
为什么 HTTPS 能够有效的解决 DNS 劫持呢?在域名被劫持的情况下,客户端访问的实际上是攻击者的 IP,客户端通过这个 IP 找到攻击者的服务器, 要求建立 HTTPS 通信,因为攻击者没有真实服务器的证书和私钥(想要申请这个域名的证书必须验证自己是这个域名的所有者,攻击者做不到这一点),把伪造 或自签名的证书提供给客户端,是得不到浏览器的认可的,浏览器会弹出不安全的警告。但是用户执意要访问我们也没办法,所以才用了有效解决而不是彻底解决这一词。
另外,就近看的话,越来越多的场合强制要求 https(比如 ios、微信小程序等等都要求 https),从长远看,升级 HTTPS 是大势所趋,一次升级,永久受益。
一句话概括就是:性能和速度下降,但是具体下降多少呢,这个不太好讲,可以延伸阅读: HTTPS 要比 HTTP 多用多少服务器资源?
HTTPS 主要分为单向认证和双向认证,99%的场景都只需要单向认证,双向认证我们后面再来讲。
在介绍 HTTPS 原理之前,我们先来假设,假如让我们自己设计一套加密方案该如何实现呢?为了更好理解,我们用人来举个栗子:

小明(客户端)暗恋小红(服务端),想给小红写信,但是又怕信在邮递的过程中被人偷看,所以他把信装在一个盒子里锁起来,信虽然是安全了,但是小红就算收到了打不开啊,把钥匙给小红寄过去?万一钥匙也被人偷去了怎么办?

就在这时,小红想到了一个办法:小红把一个没有上锁的盒子寄给小明(钥匙在小红这),小明把钥匙放进去再锁上(小明可以锁盒子,但是一旦锁上了就连 自己都打不开了),然后把这个装有钥匙的盒子寄回给小红,因为小红有钥匙,所以能打开盒子拿到里面的锁信件盒子的钥匙,再然后就能看到小明给她写的信了。 同时,小红想回信的话,也可以把信装在这个盒子里寄回去,因为这是小明的盒子,他自己肯定还有一把锁,所以他也能打开,从此,小明和小红就可以任意的写信 私通而不用担心其他人能看到了!
但是,如何保证小红寄送过来的未上锁盒子不被别人调包呢?这个等到我们介绍完 HTTPS 原理之后我们再回过头来看这个问题。
下面这张图是网上比较常见的一张图:

这个应该算是精简版,省略了一些细节,但是对于理解 https 的核心原理部分足够了。下面这张图可能更全面。

具体过程如下:
至于为什么会有三个随机数来生成最终的对称加密密钥,主要是为了进一步增大密钥的随机性, 这里 有一段不错的解释:
不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于 SSL 协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于 RSA 密钥交换算法来说,pre-master-key 本身就是一个随机数,再加上 hello 消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre master 的存在在于 SSL 协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么 pre master secret 就有可能被猜出来,那么仅适用 pre master secret 作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上 pre master secret 三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的 可不是一。
需要特别注意的是,以上握手阶段全部用的是 HTTP 协议明文传输的。
介 绍完 HTTPS 原理之后,我们再来回到小明和小红的故事来加深理解。看起来让小红寄未上锁盒子的方案很完美,但是,假如小红寄过来的盒子被人调包了呢?调 包的话打开情书的钥匙就被别人拿走了。所以这时候需要有一家在江湖上比较有威望的镖局(假设叫龙门镖局)来帮忙押送小红的盒子,当小明收到盒子时,如果是 镖局亲自送过来的、并且说是小红寄的盒子,那么小明就认为这是小红的盒子(小明很信任这家镖局),前面我们说了,这家镖局在江湖上很有威望,为了自己的声 誉它必须保证每一趟镖都没问题。有人又会问了,干嘛不直接让镖局把信送过去呢?因为小明写信不可能只写一封,而且每封情书都让镖局来押送成本太大,而且镖 局比邮局流程更繁琐,更费时间,如果小明收到一个没听过的镖局送过来的声称是小红寄的盒子,那么他不会相信。

上面例子中,小明是浏览器,小红是服务器,镖局是颁发证书的 CA(后面会提到),没听过的镖局就是不受信任的根证书,镖就是证书,信被别人看了叫 HTTP 明文泄露,信被别人改了叫 HTTP 劫持,信上的地址被人改了叫 DNS 劫持,小明寄给小红的盒子是对称加密(因为双方都有钥匙,都能打开),小红寄 给小明的没锁的盒子可以看做是非对称加密(因为所有人都可以拿它加密东西,但是一旦加密了就打不开,只有小红一个人有钥匙),镖局自己干坏事自毁前程就等 同于沃通的故事,等等。
双 向认证和单向认证原理基本差不多,主要区别是除了客户端需要认证服务端以外,服务端对客户端也需要认证。什么场景下需要验证客户端呢?比如一些银行业务, 银行会要求客户必须在电脑上插入它们签发的 U 盾之类的东西,或者安装什么控件,这里就类似客户端证书的概念,没有这个证书的人无法使用银行提供的业务。
双向认证我没有去亲自尝试,可以参考 这篇文章 。
下面还是上面那位网友画的双向认证图,我没有仔细考究,先贴在这里:

将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
那如何保证证书可信呢?证书的生成都是由国际顶级认证机构签发的,签发的时候必须验证你是不是这个域名的所有者,只有是才给你签发证书(证书和域名 一一挂钩),所以理论上其他人无法伪造你的证书(只是理论上),即使伪造了,浏览器在加载的时候会进行域名校验,如果证书中的域名和实际域名不匹配,浏览 器是会警告的。
问题又来了,浏览器怎么知道你的证书是不是合法的国际顶级认证机构签发的呢?操作系统和浏览器都内置了一些他们认为可信任的国际顶级认证机构(CA,后面会提到),只有他们签发的证书浏览器才信任。
因为非对称加密很慢,而且传输普通内容时双方都已经拿到了随机又没有其他人知道的密码,所以普通的对称加密足矣。
CA ( Certificate Authority ), 即证书认证机构,它的作用就是提供证书(即服务器证书,由域名、公司信息、序列号和签名信息组成)加强服务端和客户端之间信息交互的安全性,以及证书运维 相关服务。任何个体/组织都可以扮演 CA 的角色,难的是你要能得到全世界各大操作系统、浏览器等的默认信任,能得到他们的默认信任,你也可以自己开一家 CA 公司了,这类证书通常叫做根证书。浏览 器默认信任的 CA 大厂商有很多,比如 Symantec 赛门铁克、 Comodo 、 Godaddy 、 GolbalSign (百度微博等都是它签发的) 和 Digicert 。
如何查看操作系统内置了哪些 CA 呢? Internet 选项->内容->证书->受信任的根证书颁发机构:

想让你的 https 网站默认情况下就被全世界浏览器信任你必须使用它们签发的证书,大部分都是要钱的,当然也有免费的。
很多人一开始应该有和我一样的疑问,我升级我的 https,凭啥要到别人那里花钱买证书(即使有免费的也不爽)?,看到这里我想不用再多解释你应该懂了。
CA 必须是绝对公平公正不作恶的,否则一旦暴露出来一些丑闻,立即会被各大浏览器和厂商拉入黑名单不再信任,或者各种降级处理,带来的后果甚至可能是杀身之祸,比如被 360 收购的沃通的下场: 谷歌宣布开始全面封杀使用沃通 CA 证书网站,信誉破产的恶果 。
沃通的主要罪状有五个:
沃通为何要伪造签署日期呢?
由于如今的运算能力越来越强,SHA1 散列算法的可靠性越来越不够了。一些主流的浏览器,如果发现 2016 元旦之后签署的 CA 证书,依然采用 SHA1,会给出警告。沃通为了帮证书申请人规避浏览器警告,故意把签署日期伪造成 2015 年底。
如果顶级证书的私钥泄露,那么带来的后果是灾难性的,只能各大浏览器和操作系统通过更新补丁的方式来吊销泄密的根证书,泄露之后一般这家 CA 公司离倒闭不远了,也不是没有这样的例子:
荷兰 CA 供应商 DigiNotar 因为黑客入侵事件而成为万众瞩目的焦点,它发行的证书被众多浏览器开发商和操作系统开发商宣布为不受信任。现在, 这家失去信任的 CA 宣告破产。疑似伊朗黑客在 7 月中旬入侵了 DigiNotar 服务器,发行了 531 个伪造证书,包括了 Google、微软、雅虎、 Twitter、Facebook、中情局、军情六处和摩萨德等。DigiNotar 在 7 月 19 日发现了入侵,但直到 8 月份外界才知道入侵事件。 DigiNotar 的母公司 VASCO 承认损失惨重。
SSL 证书按大类一般可分为 DV SSL 、 OV SSL 、 EV SSL 证书,又叫域名型、企业型、增强型证书:

不论是 DV、OV 还是 EV 证书,其加密效果都是一样的,个人网站用 DV 证书、企业用 OV 足够了。
如何查看证书类型?如下图,百度是 OV, github 是 EV(其实好像并没有统一的区分方法,有的证书不会写):

一般来说,主流的 Web 服务软件,通常都基于 OpenSSL 和 Java 两种基础密码库。
详见下图:

每种格式之间都可以相互转换,转换方法可以参考阿里云给出的 帮助文档 ,在腾讯云申请证书的时候会自动帮你生成 4 种服务器各自需要的格式,非常方便。
不是说收费的证书就一定比免费证书好,只不过现阶段各 CA 机构为了自己的盈利目的,免费证书一般都有一些限制,比如只支持单域名,子域名太多的话需要挨个申请。
本文全部以 nginx 配置为例,其它 Web 服务器配置请自行 google,或者参考这篇不错的文章: SSL 证书安装指引 。
首先我们需要得到一张证书,可以申请免费的,也可以自制,本地测试时一般自制证书。
免费证书一般有下面几种:
我们一般不需要自己直接和它们打交道,国内的云服务提供商一般都有免费的证书申请,这里操作会更简单一点。
先声明一下,这里不是打广告,如果有更好用的,欢迎大家推荐。阿里云之前有一款免费的 SSL 证书申请,但是现在好像下线了,腾讯云有一款 TrustAsia 的免费证书,又拍云说是有 2 款免费证书,而且可以自动续签,但是试了一下发现必须把域名绑定到它们的 CNAME 才行,所以想想还是放弃了:

最后我用的是腾讯云 免费 SSL 证书 ,有一款 TrustAsia 的免费证书,缺点是有效期只有 1 年(到期应该可以重新申请),且不支持泛域名,如果子域名很多的话比如挨个申请,很麻烦,大家有更好用的欢迎推荐:

申请很简单,简单填写资料,然后验证域名身份,快的话 2-3 分钟内就能成功申请到:

成功后可以把证书下载到本地,内置了 Apache 、 IIS 、 Nginx 、 Tomcat 的 4 种证书格式,很方便。

申请到的证书:

虽然有很多途径可以申请到免费的证书,但一般都是单域名(不支持泛域名),限制太多,本地调试极不方便,所以我们希望本地开发时能够使用我们自己签发的证书。下面我们就来自己实践一下如何从头开始自制 HTTPS 证书。
证书一般都是用 openssl 来生成,当然也可以用 jdk 自带的 keytool 来生成,但是最终还是要用 openssl 来转换格式,所以一般还是推荐用 openssl 来生成。
首先当然还是安装 openssl ,这里我们只介绍 Windows 平台,Linux 系统的请参考 这里 ,点击 这里 下载 Win64 位的安装包:

安装很简单,安装完毕之后为了使用方便,建议配置一下环境变量:

然后就可以随时随地执行 openssl 命令了。
我们先生成一个自己的 CA 根证书 ca.crt ,然后再用这个根证书生成服务端证书 server.crt ,有人会问,为啥不直接生成服务端证书呢?因为这样做的话将来我们只要导入一个 CA 根证书,其它所有用它生成的证书都默认是可信任的,方便嘛!
好了,说了这么多废话,下面开始了:
# 生成 CA 私钥
openssl genrsa -out ca.key 1024
# 生成 CA 根证书,-day 指定证书有效期
openssl req -new -x509 -key ca.key -out ca.crt -days 365
就这么简单,第二步需要输入几个东西,虽然说乱填也可以,但建议按照提示来填,其中 Common Name 需要特别注意,如果是生成 CA 证书的话,可以输入诸如 My CA 之类的,如果是生成服务端证书的话,必须输入网站的域名,可以输入泛域名,如 *.cdsy.xyz :

另外,如果报了 unable to write 'random state' 的错误,一般都是因为没有使用管理员权限打开命令行窗口。
和 CA 证书的生成不同的时,我们需要先生成一个 csr 证书请求文件文件(CSR, Cerificate Signing Request ),有了这个文件之后再利用 CA 根证书生成最终的证书:
# 生成服务端私钥
openssl genrsa -out server.key 1024
# 生成证书请求文件
openssl req -new -key server.key -out server.csr
# 生成最终证书文件,-day 指定证书有效期
openssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt
操作截图:

但!是!各位看官先别急着去实践,以上证书我在 Chrome55 下测试没问题,但是 Chrome61 下测试提示 Subject Alternative Name missing 错误:
The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.

提示必须指定 DNS Name 或者 IP 地址,而指定这个必须要用 v3 的证书。生成 v3 证书只需要比上面多加 2 个参数 -extfile openssl.cnf -extensions v3_req ,但是多了一个额外的配置文件,这个文件里面可以填很多东西(完整配置文件参考 这里 ),我们这里仅仅需要指定 subjectAltName 即可。
先准备一个名为 openssl.cnf 的文件,内容如下:
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost.com
DNS.2 = www.localhost.com
DNS.3 = www.test123.com
#IP.1 = 127.0.0.1
解释一下,这里的 alt_names 指的是最终可以访问的域名或者 IP,所以,其实一个证书是可以多个网站同时使用的。被访问域名只要满足 DNS 和 IP 中的一个,其证书就是合法的。
然后再来重新生成证书,为了对大家产生误导,我们再来一遍完整的过程:
# 生成服务端私钥
openssl genrsa -out server.key 1024
# 生成证书请求文件
openssl req -new -key server.key -out server.csr
# 生成最终证书文件,-day 指定证书有效期
openssl x509 -req -days 365 -sha256 -CA ca.crt -CAkey ca.key -CAcreateserial -extfile openssl.cnf -extensions v3_req -in server.csr -out server.crt
双击生成的证书可以看到如下内容:

如果需要实现 HTTPS 双向认证,还要按照上述服务端一模一样的操作再生成一个 client.crt 和 client.key ,这里我们就不重述了,90%的场景都是不需要双向认证的。
上述步骤执行完之后生成了如下文件:

我们只需要双击 ca.crt 导入这个自制根证书即可,以后只要是利用这个证书生成证书浏览器都会认为是可信任的,这让我想起了 12306 干的勾当,如果你能把你这个根证书推广到全世界的电脑和手机,那么你也可以成立一家 CA 公司专门卖证书了,哈哈。
导入的时候注意证书存储位置:

由于这是很敏感的操作,所以操作系统一定会有警告:

双击 ca.crt ,会要求输入密码,输完密码就会导入证书,双击导入完的证书,选择 始终信任 即完成根证书的导入并信任:

这个标题在大约在半个月前还是正确的,但是,就在大约半个月前,12306 终于买证书了!!!倔强的 12306 坚持了这么多年推广自己的证书,终于还是妥协了:


我们还是回到 12306 没买证书以前。我们先回忆一下 12306 的做法,首页不是 https,它在首页明显位置放置了一个证书下载链接让我们去下载,真正的购票页面才开启了 https,没安装这个证书浏览器就会各种警告。
有很多人都在网上问,12306 为啥没买证书呢?是不舍得花这个钱吗?当然不是,堂堂铁老大再怎么亏损多少个亿,这点钱还是出得起的,12306 无非就是想利用自己绝对垄断地位推广自己的 SRCA 证书而已(12306 有一个中铁数字证书认证中心),至于推广证书有什么用,这不用我多说吧。
摘抄一段网友的话:
合法证书对于 SSL 加密通信和通信完整性保护的意义就是合格钥匙之于锁的关系,你把山寨 CA 请到你家浏览器的受信任 CA 列表,就相当于你从大马路上 捡把插着万能钥匙的锁回家装大门上。门上确实有锁,没钥匙确实进不来你家偷窥(机密性保护)和盗窃(完整性保护),但有万能钥匙的人呢?
Windows 平台的 nginx.exe 一般内置了 ssl 模块,无需额外单独安装。
linux 系统的 nginx 一般都是编译安装的,如果你第一次安装 nginx 的时候已经安装了 ssl 模块的话,这一步可以跳过,如果没有,继续往下看。
如何查看有没有安装 ssl 模块呢?定位到 nginx/sbin 目录执行 nginx -V 查看安装时的命令,如果有 --with-http_ssl_module ,说明已经安装了 ssl 模块,那么你可以跳过这一步了。
这里着重要讲的是已经上线运行了一段时间的 nginx 如果安装新模块。
编译安装 openssl 要很久很久,做好心理准备。
下载 openssl-1.0.2n.tar.gz 文件放在 /home/nginx/ 下面:
cd /home/nginx
tar -zxvf openssl-1.0.2n.tar.gz
cd openssl-1.0.2n
./config
make & make install
nginx 安装新模块需要整体重新编译,所以需要知道上一次安装时的编译命令,假设 nginx 安装在 /home/nginx/nginx-1.8.1 下面,定位到 sbin 下面执行 ./nginx -V (注意 V 是大写) 后可以查看安装时使用的命令:
[ root@iZ94i7kwlag Z sbin]# ./nginx -V
nginx version: nginx/1.8.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC)
built with OpenSSL 1.0.2n 7 Dec 2017
TLS SNI support enabled
configure arguments: --prefix=/home/nginx/nginx-1.8.1 --with-pcre=/home/nginx/pcre-8.38 --with-zlib=/home/nginx/zlib-1.2.8 --without-http_ssi_module
然后定位到源码包去重新编译,根据已有的命令再加上我们这次要安装的新模块命令,我这里是 --with-openssl=/home/nginx/openssl-1.0.2n --with-http_ssl_module 。注意,如果源码包删了,重新下载一个版本一致的 nginx-1.8.1.tar.gz 并解压,为了区分,我解压到 /home/nginx/temp-nginx-1.8.1 :
cd /home/nginx/temp-nginx-1.8.1
./configure --prefix=/home/nginx/nginx-1.8.1 --with-pcre=/home/nginx/pcre-8.38 --with-zlib=/home/nginx/zlib-1.2.8 --with-openssl=/home/nginx/openssl-1.0.2n --with-http_ssl_module
make
切记这里仅仅需要 make ,不需要 make install 。执行完之后我们在 /home/nginx/temp-nginx-1.8.1/objs/ 下得到了一个新的二进制文件 nginx ,上面所有操作都是为了得到这个文件,然后将这个文件覆盖现有 nginx 文件即可(为了以防万一,最好备份一下):
cd /home/nginx/nginx-1.8.1/sbin/
./nginx -s stop # 先停止
cp ./nginx ./nginx.backup # 备份
cd /home/nginx/
cp temp-nginx-1.8.1/objs/nginx nginx-1.8.1/sbin/nginx # 覆盖
然后启动 nginx 查看是否正常。
假设我们有一个 www.localhost.com 的网站,为了方便测试,把它添加到 hosts 文件中去。
然后将第一步得到的证书复制到 nginx/conf/crt/ 文件夹下(后面的 crt 是自己新建的文件夹),编辑 nginx.conf:
http {
# 省略其它配置
server {
listen 443;
server_name www.localhost.com;
ssl on;
# 书写路径时注意,即使使用了 include 将 conf 文件写到其它目录,证书路径依然是相对于 nginx.conf 而言的,且 windows 下不能以./开头
ssl_certificate crt/server.crt;
ssl_certificate_key crt/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 使用的协议
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 配置加密套件,写法遵循 openssl 标准
ssl_prefer_server_ciphers on;
location / {
root E:/github/test/dist/www;
index index.html;
}
}
}
最主要就是中间那 7 行 ssl 开头的配置,一般照着写就可以了,替换一下自己的证书路径,书写路径时注意,即使使用了 include 将 conf 文件写到其它目录,证书路径依然是相对于 nginx.conf 而言的,并且 Windows 下不能以 ./ 开头,否则会提示文件找不到。
运行 nginx -t 测试一下是否 OK:
D:\GreenSoft\nginx-1.11.8>nginx -t
nginx: the configuration file D:\GreenSoft\nginx-1.11.8/conf/nginx.conf syntax is ok
nginx: configuration file D:\GreenSoft\nginx-1.11.8/conf/nginx.conf test is successful
没问题就重启 nginx,然后打开浏览器测试,一切顺利的话你可能已经看到绿色的 https 前缀了,有木有很鸡冻:

特别注意,由于我们还没有做 http 自动跳转处理,测试时一定要主动输入 https://www.localhost.com 的完整域名!
https 页面出现 http 链接的资源我们称之为 Mixed Content (混合内容),不同浏览器对不同类型的混合内容处理方式不一样,这里我们只讲主流浏览器。现代浏览器( Chrome 、 Firefox 、 Safari 、 Microsoft Edge )基本上都遵守了 W3C 的 Mixed Content 规范,将其分为 Optionally-blockable 和 Blockable 两类:
所以网站在升级 https 时需要特别注意,既然要升级就最好要全站升级,不然很容易出现某些资源由于写死了 http:// 头导致浏览器无法加载的严重问题。当然也不是没有办法解决,可以通过 CSP 的 upgrade-insecure-requests 指令让网页所有 http 资源自动指向 https,限于篇幅,本文不对此展开讲,读者可自行了解 CSP 、 HSTS 等相关概念。
继续回到正文,当页面有 http 资源时,https 不是绿色的:


将所有 http 链接改成 https 之后,绿色回来了:

我们还是用前面的例子,假如把 nginx 中的域名改成 www.localhost2.com ,同时 hosts 也改下,但是证书不变,然后再次打开浏览器访问时提示 NET::ERR_CERT_COMMON_NAME_INVALID :

所以, DV 证书一定要是和域名挂钩的,域名不匹配浏览器会拦截。
假如我们前面自制的 CA 根证书没有导入到操作系统(模拟时可以从 Internet 选项里面找到证书入口删除之前的证书即可),然后浏览器会提示 NET::ERR_CERT_AUTHORITY_INVALID :

SHA-1 在许多安全协议中广泛使用,包括 TLS 和 SSL、PGP、SSH、S/MIME 和 IPsec。早在 2005 年,密码学家就证明 SHA-1 的破解速度比预期提高了 2000 倍,虽然破解仍然是极其困难和昂贵的,但随着计算机变得越来越快和越来越廉价,SHA-1 算法的安全性也逐年降低,已被密码学家严重质疑。所以,各大浏览器 相继宣布(link:https://www.chinassl.net/?f=announcements&a=view&r=647) 将逐步停止对 SHA-1 签名证书的支持。

所以在生成证书时一定要指定 -sha256 参数。
更换证书时最好重启一下 nginx 然后多刷新 2 次防止未生效折腾半天;
网站是升级 https 了,但是用户又不知道,而且不输入协议头的话浏览器默认都是按照 http 来加载,所以我们还要对 http 做 301 自动跳转处理。
server {
listen 80;
server_name www.localhost.com;
location / {
rewrite ^(.*) https://www.localhost.com $document_uri permanent;
}
}
上述自动跳转配置会自动携带 URL 和参数,例如,访问 http://www.localhost.com/test.html?a=1 会自动跳转到 https://www.localhost.com/test.html?a=1 。`
这个根据实际网站的不同难易程度会有很大不同,例如,如果你是全站升级 https,包括各类子域名,那么很简单,全文搜索 http,批量替换成 // 开头即可。但是如果只是部分升级,比如主站升级了,子站没升级,然后主站页面又有各种子站的资源链接、跳转等,这就麻烦了,这时候只能手动处理了。特别是针对子域名比较多的网站,免费证书又都不支持泛域名,全部升级需要一个个去生成证书,然后配置,麻烦死了。
另外,为了安全起见,刚开始的时候建议静态资源的站点(比如图片,很有可能外站引用了这个图片链接)同时保持 http 和 https 都可以访问,等时间成熟了再下掉或者做 301 重定向,否则很容易出现某个资源加载不了导致 404 的问题。
升级 https 最麻烦的可能就是这一步了,特别是对于历史悠久的大网站,当然小网站就另当别论了。
CSP ,全称 Content Security Policy ,意即内容安全策略,有非常多的指令,用来实现各种各样与页面内容安全相关的功能,这里介绍 2 个比较有用的。所有 CSP 指令都有 2 种启用方式,一种是 HTTP 头部,一种是 <meta> 标签。
前面说过,对于 HTTPS 页面中的图片等资源浏览器默认会加载,仅仅是控制台给一个警告,因为图片类资源被劫持通常问题不会太大,但有些页面按钮布局等是用图片做的,图片被篡改会影响用户使用,最重要的,页面只要有一个 http 的链接,地址栏的 https 就不是绿色的。
可以通过 CSP 的 block-all-mixed-content 指令让页面进入对 Mixed Content 的严格检测( Strict Mixed Content Checking )模式。在这种模式下,所有非 HTTPS 资源都被禁止加载,控制台报错,https 回归绿色。
HTTP 响应头方式:
Content-Security-Policy: block-all-mixed-content
HTML 标签方式:
<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
HTTP 迁移 HTTPS 对于一些大型网站来说工作量巨大,难免有疏漏的地方,此时可以让浏览器帮我们做 http 自动跳转 https 这一步。通过 upgrade-insecure-requests 这个 CSP 指令,可以让浏览器帮忙做这个转换,启用这个策略后,不仅是 http 静态资源,http 接口调用也会自动转成 https 再发出去。
这里只介绍 HTML 标签方式:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
效果如下图, http 的图片自动变成 https :

99% 的人访问一个网站都不会主动输入 http 前缀, https 就更是如此,因为用户又不知道你的网站是不是 https 的,所以几乎所有的 https 网站都是通过 http 的 301/302 自动跳转的方式来让用户进入。注意,由于第一次是 http 访问,如果第一次访问就被劫持那用户根本到不了 https 页面,这就叫 https 降级劫持。
这个问题可以通过 HSTS ( HTTP Strict Transport Security )来解决。 HSTS 是一个响应头,格式如下:
Strict-Transport-Security: max-age=expireTime [; includeSubDomains] [; preload]
注意 HSTS 这个响应头是添加到 HTTPS 响应头中而不是 HTTP 响应的,这可能和我们预期的不一样,如果一个 HTTPS 网站从来没被访问过,那这个 HSTS 头永远生效不了,那怎么办呢?浏览器厂商们为了解决这个问题,提出了一个 HSTS Preload List 方案:内置一份可以定期更新的启用 HSTS 的域名列表,对于列表中的域名,即使用户之前没有访问过,也会使用 HTTPS 协议。看起来这个主意挺馊的,但这也是没办法的办法。想要加入这个列表需要很多强制要求,而且满足了也不一定能申请成功,我们这里就不细讲了。
另外,启用 HSTS 必须使用默认的 443 端口;必须使用域名,不能是 IP。而且启用 HSTS 之后,一旦网站证书错误,用户无法选择忽略。
建议:只要你不能确保永远提供 HTTPS 服务,就不要启用。因为一旦 HSTS 生效,你再想把网站重定向为 HTTP,之前的老用户会被无限重定向,唯一的办法是换新域名。
默认情况下 Fiddler 不会对 HTTPS 网站抓包,要启用的话步骤如下:
Tool -> Fiddler Options :

然后在 IE 代理中启用对 HTTPS 的代理(默认仅开启了 HTTP):

Fiddler 会为每一个网站动态颁发一个 *.xxx.com 的证书,想要看网站真正证书时需关闭 Fiddler:

所以,如果开发时使用 Fiddler 做代理的话,我们上面那么一大段自己生成证书的步骤可以省略了,哈哈,当然自己掌握了这个过程也不多余。
需求:手机访问电脑本机的某个 HTTPS 网站,而且是用了 Fiddler 的 Willow 插件修改了域名的网站。
手机上长按已经连接的 WIFI->修改网络->高级->手动设置代理,代理地址就是你电脑的局域网 IP,端口就是上面的 8888,保存。

此时直接访问是不行的,因为 Fiddler 默认禁用了远程访问,开启方法如下,注意勾选之后必须重启 Fiddler ,否则不会生效(开始还以为是防火墙问题):

此时使用手机访问 HTTP 网站没问题,但是访问 HTTPS 时会提示证书不受信任,这是因为还没有安装 Fiddler 的根证书( Fiddler 对 HTTPS 进行代理时会使用自己的根证书对每一个访问的网站动态生成证书,具体细节可以自行百度),手机访问 http://你的 IP:8888 ,点击页面的 FiddlerRoot certificate 链接下载安装证书,安装成功后即可顺利访问部署在电脑本机的 HTTPS 网站了。

