HTTPS从认识到线上实战全记录
本文由 小茗同学 发表于 2017-12-19 浏览(12123)
最后修改 2019-07-10 标签:https http 证书

前言

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

[TOC]

原理篇

认识HTTPS

先说一下,本文可能有些地方由于描述不到位或者我本人理解错误而出现不准确内容,有错误欢迎指正!

什么是HTTPS

HTTPS全称Hyper Text Transfer Protocol over Secure Socket Layer,直译过来就是通过SSL实现的超文本传输协议,简单来讲就是加密版的HTTP协议,也就是HTTP+SSL/TLS

为什么需要加密版的HTTP呢,因为我们都知道,HTTP是明文传输的,因此使用HTTP协议传输隐私信息非常不安全,很容易在传输过程中被窃取,或者通信内容被人篡改冒充,使用HTTPS可以避免这些问题。

SSL/TLS

为了解决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的优点

从底层分析HTTPS有以下3大优点:

  1. 防窃听:信息被加密了,第三方拿不到密码,所以可以防窃听;
  2. 防篡改:还是因为信息被加密了,如果执意篡改的话,客户端无法解密不说,而且数据完整性校验这一关也过不了;
  3. 防冒充:防止攻击者冒充他人身份参与通信,比如HTTPS的双向认证;

从实际出发来看,HTTPS主要有以下优点:

  1. 防止私密信息泄露,防止信息被篡改;
  2. 有助于SEO,百度、谷歌均明确表示会优先收录、展示HTTPS站点的内容;
  3. 完全杜绝运营商HTTP劫持问题;
  4. 有效解决运营商DNS劫持问题,降低网站被劫持的风险;
  5. HTTPS的小绿锁表示可以提升用户对网站信任程度(当然不是说有小绿锁的都是安全的);
  6. 可以有效防止山寨、镜像网站等;
  7. 为未来升级HTTP/2做准备,HTTP/2必须基于HTTPS部署;

下面单独对3和4说一下。

运营商HTTP劫持我们都知道,经常莫名奇妙的在自己网站上看到各种恶意的恶心广告,查看代码发现被注入了一些奇奇怪怪的js,这其实就是HTTP劫持,启用了HTTPS之后,我们传输的内容都是加密的,运营商想篡改内容都没辙。

为什么HTTPS能够有效的解决DNS劫持呢?在域名被劫持的情况下,客户端访问的实际上是攻击者的IP,客户端通过这个IP找到攻击者的服务器,要求建立HTTPS通信,因为攻击者没有真实服务器的证书和私钥(想要申请这个域名的证书必须验证自己是这个域名的所有者,攻击者做不到这一点),把伪造或自签名的证书提供给客户端,是得不到浏览器的认可的,浏览器会弹出不安全的警告。但是用户执意要访问我们也没办法,所以才用了有效解决而不是彻底解决这一词。

另外,就近看的话,越来越多的场合强制要求https(比如ios、微信小程序等等都要求https),从长远看,升级HTTPS是大势所趋,一次升级,永久受益。

HTTPS的缺点

  • 服务器性能下降,开启HTTPS会增加内存、CPU、网络带宽的开销,特别是非对称加密这一块;
  • 访问速度下降,HTTP连接的建立需要3次握手,HTTPS还需要加上ssl的几次握手(具体是几次没去考究,有说是9次),当然下降主要是在第一次建立连接的时候,后面正常通信速度一般没啥变化;
  • 除了握手部分外,所有信息传输之后浏览器和服务器都要进行加密解密,又是一笔额外的开销;
  • 申请证书需要一笔花费,当然现在免费证书也很容易申请到,这个不算明显缺点;

一句话概括就是:性能和速度下降,但是具体下降多少呢,这个不太好讲,可以延伸阅读:HTTPS 要比 HTTP 多用多少服务器资源?

HTTPS原理

HTTPS主要分为单向认证和双向认证,99%的场景都只需要单向认证,双向认证我们后面再来讲。

小明和小红的故事

在介绍HTTPS原理之前,我们先来假设,假如让我们自己设计一套加密方案该如何实现呢?为了更好理解,我们用人来举个栗子:

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

就在这时,小红想到了一个办法:小红把一个没有上锁的盒子寄给小明(钥匙在小红这),小明把钥匙放进去再锁上(小明可以锁盒子,但是一旦锁上了就连自己都打不开了),然后把这个装有钥匙的盒子寄回给小红,因为小红有钥匙,所以能打开盒子拿到里面的锁信件盒子的钥匙,再然后就能看到小明给她写的信了。同时,小红想回信的话,也可以把信装在这个盒子里寄回去,因为这是小明的盒子,他自己肯定还有一把锁,所以他也能打开,从此,小明和小红就可以任意的写信私通而不用担心其他人能看到了!

但是,如何保证小红寄送过来的未上锁盒子不被别人调包呢?这个等到我们介绍完HTTPS原理之后我们再回过头来看这个问题。

单向认证

下面这张图是网上比较常见的一张图:

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

具体过程如下:

  1. 浏览器访问一个https地址,同时向服务端发送支持的SSL协议版本、支持的加密算法、一个客户端生成的随机数(用于稍后生成”对话密钥”)等。
  2. 服务端给客户端返回要使用的SSL协议版本号、加密算法、一个服务器生成的随机数(也同样用于稍后生成”对话密钥”),另外还有一个最重要的,返回服务器端的证书,即公钥证书;
  3. 客户端收到服务器回应以后,首先验证证书是否合法,如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期、或者返回的公钥不能正确解开返回证书中的数字签名,就会向访问者显示一个警告,由其选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥继续;
  4. 客户端向服务端发送自己所能支持的对称加密方案(注意是对称加密),供服务器端进行选择;
  5. 服务器端在客户端提供的加密方案中选择加密程度最高的加密方式;
  6. 服务器将选择好的加密方案通过明文方式返回给客户端;
  7. 客户端接收到服务端返回的加密方式后,使用该加密方式生成产生一个随机密钥(后续用作通信过程中对称加密的密钥),然后将该随机数用用证书中的公钥加密发给服务端;
  8. 服务器收到客户端返回的加密信息后,使用自己的私钥进行解密,获得最终的对称加密密钥。至此,客户端和服务端都得到一个随机密钥,并且这个密钥别人没法知道;
  9. 在接下来的会话中,服务器和客户端将会使用该密码进行对称加密解密,保证通信过程中信息的安全。

至于为什么会有三个随机数来生成最终的对称加密密钥,主要是为了进一步增大密钥的随机性,这里有一段不错的解释:

不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于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

何为CA

CA(Certificate Authority),即证书认证机构,它的作用就是提供证书(即服务器证书,由域名、公司信息、序列号和签名信息组成)加强服务端和客户端之间信息交互的安全性,以及证书运维相关服务。任何个体/组织都可以扮演 CA 的角色,难的是你要能得到全世界各大操作系统、浏览器等的默认信任,能得到他们的默认信任,你也可以自己开一家CA公司了,这类证书通常叫做根证书。浏览器默认信任的 CA 大厂商有很多,比如 Symantec赛门铁克、ComodoGodaddyGolbalSign(百度微博等都是它签发的) 和 Digicert

如何查看操作系统内置了哪些CA呢?Internet选项->内容->证书->受信任的根证书颁发机构:

想让你的https网站默认情况下就被全世界浏览器信任你必须使用它们签发的证书,大部分都是要钱的,当然也有免费的。

很多人一开始应该有和我一样的疑问,我升级我的https,凭啥要到别人那里花钱买证书(即使有免费的也不爽)?,看到这里我想不用再多解释你应该懂了。

CA如果作恶怎么办

CA必须是绝对公平公正不作恶的,否则一旦暴露出来一些丑闻,立即会被各大浏览器和厂商拉入黑名单不再信任,或者各种降级处理,带来的后果甚至可能是杀身之祸,比如被360收购的沃通的下场:谷歌宣布开始全面封杀使用沃通CA证书网站,信誉破产的恶果

沃通的主要罪状有五个:

  1. 秘密收购自己的根CA公司startcom;
  2. 只要有子域名的权限就可以得到顶级域名的权限,这个很危险,比如github是开放子域名给任何用户的;
  3. 伪造证书签署日期;
  4. 签署多个相同序列号证书;
  5. 邮件恐吓使用其它免费证书的用户说他们的证书不安全;

沃通为何要伪造签署日期呢?

由于如今的运算能力越来越强,SHA1 散列算法的可靠性越来越不够了。一些主流的浏览器,如果发现2016元旦之后签署的 CA 证书,依然采用 SHA1,会给出警告。沃通为了帮证书申请人规避浏览器警告,故意把签署日期伪造成2015年底。

顶级证书私钥泄露怎么办

如果顶级证书的私钥泄露,那么带来的后果是灾难性的,只能各大浏览器和操作系统通过更新补丁的方式来吊销泄密的根证书,泄露之后一般这家CA公司离倒闭不远了,也不是没有这样的例子:

荷兰CA供应商DigiNotar因为黑客入侵事件而成为万众瞩目的焦点,它发行的证书被众多浏览器开发商和操作系统开发商宣布为不受信任。现在,这家失去信任的CA宣告破产。疑似伊朗黑客在7月中旬入侵了DigiNotar服务器,发行了531个伪造证书,包括了Google、微软、雅虎、Twitter、Facebook、中情局、军情六处和摩萨德等。DigiNotar在7月19日发现了入侵,但直到8月份外界才知道入侵事件。DigiNotar的母公司VASCO承认损失惨重。

SSL证书

证书的种类

SSL 证书按大类一般可分为DV SSLOV SSLEV SSL证书,又叫域名型、企业型、增强型证书:

  • DV SSL(Domain Validation),域名型SSL证书,证书颁布机构只对域名的所有者进行在线检查,只要你能证明你是这个域名的所有者就可以给你颁发证书,不会校验网站的资质,哪怕你是个黄赌毒网站也可以,个人网站、非盈利项目、开源项目等用域名型证书足矣;
  • OV SSL(Organization Validation),企业型SSL证书,需要购买者提交组织机构资料和单位授权信等在官方注册的凭证,证书颁发机构在签发 SSL 证书前不仅仅要检验域名所有权,还必须对这些资料的真实合法性进行多方查验,只有通过验证的才能颁发 SSL 证书;
  • EV SSL(Extended Validation),增强型SSL证书(EV SSL),验证流程更加具体详细,验证步骤更多,这样一来证书所绑定的网站就更加的可靠、可信。它跟普通SSL证书的区别是浏览器的地址栏变绿,如果是不受信的 SSL 证书则拒绝显示,如果是钓鱼网站,地址栏则会变成红色,以警示用户。

不论是 DV、OV 还是 EV 证书,其加密效果都是一样的,个人网站用DV证书、企业用OV足够了。

如何查看证书类型?如下图,百度是OV,github是EV(其实好像并没有统一的区分方法,有的证书不会写):

证书的格式

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

  • TomcatWeblogicJBossWeb服务软件,一般使用JDK自带的Keytool工具,生成Java Keystore(JKS)格式的证书文件。
  • ApacheNginxWeb服务软件,一般使用OpenSSL工具提供的密码库,生成 .key.crt等格式的证书文件。
  • IBM 的 Web 服务产品,如 Websphere、IBM Http Server(IHS)等,一般使用 IBM 产品自带的 iKeyman 工具,生成 KDB 格式的证书文件。
  • 微软的IIS使用Windows自带的证书库生成pfx格式的证书文件。

详见下图:

每种格式之间都可以相互转换,转换方法可以参考阿里云给出的帮助文档,在腾讯云申请证书的时候会自动帮你生成4种服务器各自需要的格式,非常方便。

证书的收费与免费

不是说收费的证书就一定比免费证书好,只不过现阶段各CA机构为了自己的盈利目的,免费证书一般都有一些限制,比如只支持单域名,子域名太多的话需要挨个申请。

实践篇

本文全部以nginx配置为例,其它Web服务器配置请自行google,或者参考这篇不错的文章:SSL证书安装指引

第一步,生成证书

首先我们需要得到一张证书,可以申请免费的,也可以自制,本地测试时一般自制证书。

申请免费证书

免费证书一般有下面几种:

  • Let's Encrypt:获得Mozilla、微软等主要浏览器厂商的根授信,出现的目的就是为了推广HTTPS,缺点是只有3个月有效期,过期需续签;
  • TrustAsia亚洲诚信;
  • StartSSL免费DV证书:因丑闻被主流浏览器封杀,一般不建议;
  • Wosign(沃通)免费DV证书:同上,被各大浏览器封杀,基本可以无视了;

我们一般不需要自己直接和它们打交道,国内的云服务提供商一般都有免费的证书申请,这里操作会更简单一点。

先声明一下,这里不是打广告,如果有更好用的,欢迎大家推荐。阿里云之前有一款免费的SSL证书申请,但是现在好像下线了,腾讯云有一款TrustAsia的免费证书,又拍云说是有2款免费证书,而且可以自动续签,但是试了一下发现必须把域名绑定到它们的CNAME才行,所以想想还是放弃了:

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

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

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

申请到的证书:

自制证书

虽然有很多途径可以申请到免费的证书,但一般都是单域名(不支持泛域名),限制太多,本地调试极不方便,所以我们希望本地开发时能够使用我们自己签发的证书。下面我们就来自己实践一下如何从头开始自制HTTPS证书。

证书一般都是用openssl来生成,当然也可以用jdk自带的keytool来生成,但是最终还是要用openssl来转换格式,所以一般还是推荐用openssl来生成。

安装openssl

首先当然还是安装openssl,这里我们只介绍Windows平台,Linux系统的请参考这里,点击这里下载Win64位的安装包:

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

然后就可以随时随地执行openssl命令了。

生成CA根证书

我们先生成一个自己的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之类的,如果是生成服务端证书的话,必须输入网站的域名,可以输入泛域名,如*.haoji.me

另外,如果报了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.crtclient.key,这里我们就不重述了,90%的场景都是不需要双向认证的。

Windows导入证书

上述步骤执行完之后生成了如下文件:

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

导入的时候注意证书存储位置:

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

Mac导入证书

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

引申话题:12306为啥不申请证书

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

我们还是回到12306没买证书以前。我们先回忆一下12306的做法,首页不是https,它在首页明显位置放置了一个证书下载链接让我们去下载,真正的购票页面才开启了https,没安装这个证书浏览器就会各种警告。

有很多人都在网上问,12306为啥没买证书呢?是不舍得花这个钱吗?当然不是,堂堂铁老大再怎么亏损多少个亿,这点钱还是出得起的,12306无非就是想利用自己绝对垄断地位推广自己的SRCA证书而已(12306有一个中铁数字证书认证中心),至于推广证书有什么用,这不用我多说吧。

摘抄一段网友的话:

合法证书对于SSL加密通信和通信完整性保护的意义就是合格钥匙之于锁的关系,你把山寨CA请到你家浏览器的受信任CA列表,就相当于你从大马路上捡把插着万能钥匙的锁回家装大门上。门上确实有锁,没钥匙确实进不来你家偷窥(机密性保护)和盗窃(完整性保护),但有万能钥匙的人呢?

第二步,nginx的ssl模块安装

Windows系统

Windows平台的nginx.exe一般内置了ssl模块,无需额外单独安装。

Linux系统

linux系统的nginx一般都是编译安装的,如果你第一次安装nginx的时候已经安装了ssl模块的话,这一步可以跳过,如果没有,继续往下看。

如何查看有没有安装ssl模块呢?定位到nginx/sbin目录执行nginx -V查看安装时的命令,如果有--with-http_ssl_module,说明已经安装了ssl模块,那么你可以跳过这一步了。

这里着重要讲的是已经上线运行了一段时间的nginx如果安装新模块,其实下面的内容在我另外一篇文章中有提到过。

编译安装openssl

编译安装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安装新模块需要整体重新编译,所以需要知道上一次安装时的编译命令,假设nginx安装在/home/nginx/nginx-1.8.1下面,定位到sbin下面执行./nginx -V(注意V是大写)后可以查看安装时使用的命令:

[root@iZ94i7kwlagZ 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查看是否正常。

第三步,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的完整域名!

常见问题

Mixed Content

https页面出现http链接的资源我们称之为Mixed Content(混合内容),不同浏览器对不同类型的混合内容处理方式不一样,这里我们只讲主流浏览器。现代浏览器(ChromeFirefoxSafariMicrosoft Edge)基本上都遵守了W3CMixed Content规范,将其分为Optionally-blockableBlockable两类:

  • Optionally-blockable主要是只危险较小的图片、视频、音频等资源,这类资源即使被人篡改也不会有太大的问题,浏览器默认会加载,展示在控制台会打印警告信息,并且https不是绿色的。
  • Blockable一般是指除了上述之外所有的被限制加载的http资源,如JSCSSiframe等,浏览器会直接禁止加载,并且控制台打印错误信息。

所以网站在升级https时需要特别注意,既然要升级就最好要全站升级,不然很容易出现某些资源由于写死了http://头导致浏览器无法加载的严重问题。当然也不是没有办法解决,可以通过CSPupgrade-insecure-requests指令让网页所有http资源自动指向https,限于篇幅,本文不对此展开讲,读者可自行了解CSPHSTS等相关概念。

继续回到正文,当页面有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签名的证书

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

使用SHA-1算法浏览器会给出警告

所以在生成证书时一定要指定-sha256参数。

证书刷新不及时

更换证书时最好重启一下nginx然后多刷新2次防止未生效折腾半天;

第四步,http自动跳转

网站是升级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

CSP,全称Content Security Policy,意即内容安全策略,有非常多的指令,用来实现各种各样与页面内容安全相关的功能,这里介绍2个比较有用的。所有CSP指令都有2种启用方式,一种是HTTP头部,一种是<meta>标签。

禁用http链接

前面说过,对于HTTPS页面中的图片等资源浏览器默认会加载,仅仅是控制台给一个警告,因为图片类资源被劫持通常问题不会太大,但有些页面按钮布局等是用图片做的,图片被篡改会影响用户使用,最重要的,页面只要有一个http的链接,地址栏的https就不是绿色的。

可以通过CSPblock-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对于一些大型网站来说工作量巨大,难免有疏漏的地方,此时可以让浏览器帮我们做http自动跳转https这一步。通过upgrade-insecure-requests这个CSP指令,可以让浏览器帮忙做这个转换,启用这个策略后,不仅是http静态资源,http接口调用也会自动转成https再发出去。

这里只介绍HTML标签方式:

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

效果如下图,http的图片自动变成https

HSTS:防止HTTPS降级劫持

99%的人访问一个网站都不会主动输入http前缀,https就更是如此,因为用户又不知道你的网站是不是https的,所以几乎所有的https网站都是通过http301/302自动跳转的方式来让用户进入。注意,由于第一次是http访问,如果第一次访问就被劫持那用户根本到不了https页面,这就叫https降级劫持。

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

Strict-Transport-Security: max-age=expireTime [; includeSubDomains] [; preload]
  • max-age,单位是秒,用来告诉浏览器在指定时间内这个网站必须通过 HTTPS 协议来访问,比如说我设置一年,那么一年以内即使用户输入http并且被劫持了,浏览器依然会以HTTPS的方式来访问;
  • includeSubDomains,可选参数,如果指定这个参数,表明这个网站所有子域名也必须通过 HTTPS 协议来访问。
  • preload,可选参数,是否启用preload list,关于这个是什么后面再介绍。

注意HSTS这个响应头是添加到HTTPS响应头中而不是HTTP响应的,这可能和我们预期的不一样,如果一个HTTPS网站从来没被访问过,那这个HSTS头永远生效不了,那怎么办呢?浏览器厂商们为了解决这个问题,提出了一个HSTS Preload List方案:内置一份可以定期更新的启用HSTS的域名列表,对于列表中的域名,即使用户之前没有访问过,也会使用 HTTPS 协议。看起来这个主意挺馊的,但这也是没办法的办法。想要加入这个列表需要很多强制要求,而且满足了也不一定能申请成功,我们这里就不细讲了。

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

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

Fiddler抓包HTTPS

默认情况下Fiddler不会对HTTPS网站抓包,要启用的话步骤如下:

Tool -> Fiddler Options

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

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

所以,如果开发时使用Fiddler做代理的话,我们上面那么一大段自己生成证书的步骤可以省略了,哈哈,当然自己掌握了这个过程也不多余。

手机抓包HTTPS

需求:手机访问电脑本机的某个HTTPS网站,而且是用了FiddlerWillow插件修改了域名的网站。

手机上长按已经连接的WIFI->修改网络->高级->手动设置代理,代理地址就是你电脑的局域网IP,端口就是上面的8888,保存。

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

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

参考

关于tomcat开启HTTPS可以参考我之前写的一篇文章:HTTPS工作原理以及Tomcat开启HTTPS单向认证

以下为本文写作过程中参考过的部分文章: