https到底是如何防篡改的

1.前言

https是一个老生常谈的话题了,也是面试过程种经常甚至必然会问到的一个问题 但当问到https为什么安全的时候,很多人的回答就是简单的回一句:因为他加密了!然后就没然后了!你也相当于啥都没回答出来!

2.我为什么要写这篇文章呢?

网上的文章有的不太全面或者不易懂,有的甚至理解有偏差,有可能会给学习的人造成更多的疑惑

3.说https之前,先说一下http为什么不安全,体现在哪里?怎么演示出来?

大家都知道,http是明文传输,但是也有不少人这样想:虽然是明文传输,但我也没遇到什么不安全的问题呀,所以我也没必要搞https!

我只能给出以下几点解释

1.你很幸运,没被盯上

2.你的网站没啥流量,搞你没啥价值

3.你的账号密码等隐私信息已经被窃取,而你依然浑然不知

接下来我给大家演示一下http为什么不安全

大家都连接过公共wifi吧,这里我就用电脑开个热点,然后用手机连接热点,然后用手机访问一个http的登录界面,并输入账号密码登录,我在电脑用wireshark抓包

第一步:电脑开启热点,并打开wireshark准备抓包

第二步:手机连接热点

第三步:手机访问登录页并登录

第四步:查看电脑抓的包

问题是不是很明白了,你的账号密码都是明文传输的(密码是因为前端md5之后传的),中间的wifi(准确说是路由器)是直接可以截取查看的,能截取查看,是不是就可以篡改?答案当然是肯定的

现在试想一下,如果你连接的wifi甚至你本地的运营商就有问题(为了利益),那他是不是就可以为所欲为了

比如:

1.窃取你的隐私信息

2.修改服务端返回的信息,加入一个js广告文件,此时广告就满天飞了

3.修改服务端返回信息,直接给你重定向到钓鱼或者广告网站,想一想你有没有遇到过类似情况:你打开的百度,却跳转到了一个”你懂的“的网站

4.修改服务端返回信息,你突然发现你平时访问的网站角落里突然出现了个“特别吸引眼球的美女”

说到这,是不是足以说明http本身确实存在安全性问题。如果你的回答依然是否定的,建议多看几遍上面的内容,给自己洗洗脑!

4.https为什么就安全了?他是怎么保证安全的?

想到安全,大家首先会想到,那就加密呗!所以引来第一个问题,如何加密 在介绍如何加密问题前,先简单介绍一下加密的大致种类

加密的大致种类:

  1. 不可逆加密。 比如 MD5、SHA、HMAC

    典型用途: 密码总不能明文存到数据库吧,所以一般加密存起来,只要用户的输入经过同样的加密算法 对比一下就知道密码是否正确了,所以没必要可逆

  2. 可逆加密
    • 对称加密。比如:AES、DES、3DES、IDEA、RC4、RC5、RC6

      典型用途: 用同一个密码加密和解密,太常见了,我用密码加密文件发给你,你只能用我的密码才能解开

    • 非对称加密(就是公私钥)。比如:RSA、DSA、ECC

      典型用途: 1.加密(保证数据安全性)使用公钥加密,需使用私钥解密。 2.认证(用于身份判断)使用私钥签名,需使用公钥验证签名。

介绍完加密种类,接下来说说如何加密

用不可逆加密可行吗?

首先不可逆加密的是不是可以直接排除了,不知道为啥的,可以想一想自己的目的是什么哈,如果还不知道,可以打120了

用对称加密可行吗?

如果通信双方都各自持有同一个密钥,且没有别人知道,这两方的通信安全当然是可以被保证的,然而最大的问题就是这个密钥怎么让传输的双方知晓,同时不被别人知道,想一想:是不是不管怎么传,中间都有可能被截获,密钥都被截获了,其他的安全是不是也就无从谈起了。看来纯粹的对称加密不能解决http的安全问题

用非对称加密(rsa)可行吗?

试想一下:如果服务器生成公私钥,然后把公钥明文给客户端(有问题,下面说),那客户端以后传数据用公钥加密,服务端用私钥解密就行了,这貌似能保证浏览器到服务端的通道是安全的,那服务端到浏览器的通道如何保证安全呢?

那既然一对公私钥能保证,那如果浏览器本身也生成一对公私钥匙,然后把公钥明文发给服务端,抛开明文传递公钥的问题,那以后是不是可以安全通信了,的确可以!但https本身却不是这样做的,最主要的原因是非对称加密非常耗时,特别是加密解密一些较大数据的时候有些力不从心,当然还有其他原因。既然非对称加密非常耗时,那只能再考虑对称加密了

 HTTPS 握手使用的非对称加密算法非常消耗CPU 资源,据测试,13K 左右的握手QPS 就把一台32核机器 CPU 资源打爆:

用非对称加密 + 对称加密可行吗?(行也得行,不行也得行,因为也没有其他方式了)

上面提到浏览器拥有服务器的公钥,那浏览器生成一个密钥,用服务器的公钥加密传给服务端,然后服务端和浏览器以后拿这个密钥以对称加密的方式通信不就好了!完美!

所以接下来说一下上面遗留的一个问题:服务端的公钥是明文传过去的,有可能导致什么问题呢?

如果服务端在把明文公钥传递给浏览器的时候,被黑客截获下来,然后把数据包中的公钥替换成自己伪造的公钥(当然他自己有自己的私钥),浏览器本身是不知道公钥真假的,所以浏览器还是傻傻的按照之前的步骤,生成对称密钥,然后用假的公钥加密传递给服务端,这个时候,黑客截获到报文,然后用自己的私钥解密,拿到其中的对称密钥,然后再传给服务端,就这样神不知鬼不觉的,对称密钥被黑客截取,那以后的通信其实就是也就全都暴露给黑客了。

卧槽,这也不行,那也不行,到底咋办?

淡定的考虑一下,上面的流程到底哪里存在问题,以致使黑客可以任意冒充服务端的公钥! 其实根本原因就是浏览器无法确认自己收到的公钥是不是网站自己的

如何保证浏览器收到的公钥一定是该网站的公钥

现实生活中,如果想证明某身份证号一定是小明的,怎么办?看身份证。这里国家机构起到了“公信”的作用,身份证是由它颁发的,它本身的权威可以对一个人的身份信息作出证明。互联网中能不能搞这么个公信机构呢?给网站颁发一个“身份证”?当然可以,这就是平时经常说的数字证书

什么是数字证书?证书都包含什么?

身份证之所以可信,是因为背后是国家,那数字证书如何才可信呢?这个时候找CA(Certificate Authority)机构。办身份证需要填写自己的各种信息,去CA机构申请证书需要什么呢?至少应该有以下几项吧

  1. 网站域名
  2. 证书持有者
  3. 证书有效期
  4. 证书颁发机构
  5. 服务器公钥(最主要)
  6. 接下来要说的签名时用的hash算法

从上图我们能看到证书包含以下内容:

(1) Validity也即有效期,有效期包含生效时间和失效时间,是一个时间区间;

(2) 公钥信息Subject Public Key Info,包括公钥的加密算法和公钥内容;

(3) Fingerprints信息,fingerprints用于验证证书的完整性;

(4) 证书的签名Certificate Signature Value和Certificate Signature Algorithm,对证书签名所使用的Hash算法和Hash值;

(5) 签发该证书的CA机构Issuer;

(6) 该证书是签发给哪个组织/公司信息Subject;

(7) 证书版本Version、证书序列号Serial Number以及Extensions扩展信息等。

 那证书如何安全的送达给浏览器,如何防止被篡改呢?给证书盖个章(防伪标记)不就好了,这就又引出另外一个概念:数字签名

什么是数字签名?签名的过程是什么

签名的过程其实也很简单

  1. CA机构拥有非对称加密的私钥和公钥
  2. CA对证书明文信息进行hash
  3. 对hash后的值用私钥加密,得到数字签名

所以呢,总结一下:CA机构颁发的证书包含(证书内容的明文+签名)

浏览器收到服务下发的证书之后,拿到证书明文和签名,怎么验证是否篡改了呢?

大家知道,私钥签名,公钥验签。证书里面的签名是CA机构用私钥签名的,所以我只要用CA机构的公钥验证一下签名不就好了,怎么验证呢?

还记得证书里面的明文包含什么吧,不记得的话看看上面的内容。

  1. 拿到证书里面明文的hash算法并对明文内容进行hash运算,得到A
  2. 用CA的公钥解密签名得到B
  3. 比较A 和 B,如果相等,说明没有被篡改,否则浏览器提示证书不可信

有没有发现一个问题?CA的公钥从哪里获取呢

这个简单,CA权威机构本来也没多少个,所以,浏览器内部都内置了各大CA机构的公钥信息

证书中的指纹和签名的用处分别是什么

签名和指纹完全是两码事。对于证书的强度拇指指纹是无关的,但签名是相关的。

证书的签名是颁发者通过签名证书创建的东西。TLS堆栈(浏览器内部)使用此签名来验证信任链。这里使用一个强大的算法很重要,这样就没有人可以创建一个假证书,它看起来像是由受信任的颁发者(CA)签名的。

指纹只是证书上的散列。主要用于人工接收,检查证书是否为预定证书,比如 打电话给 CA认证机构 并说出指纹进行核对。  浏览器是通过签名来验证证书的有效性的,浏览器不会关注指纹。

简单总结一下

1.如果证书被篡改,浏览器就提示不可信,终止通信,如果验证通过,说明公钥没问题,一定没被篡改

2.公钥没被篡改,那浏览器生成的对称加密用的密钥用公钥加密发送给服务端,也只有服务端的私钥能解开,所以保证了 对称密钥不可能被截获,对称密钥没被截获,那双方的通信就一定是安全的

3.黑客依然可以截获数据包,但是全都是经过对称加密的密钥加密的,他也可以篡改,只是没有任何意义了,黑客也不会做吃力不讨好的事了

5. 证书链

上面提到的CA颁发证书,实际证书申请中,由于权威的CA机构数量不多,若所有的服务器证书都向权威CA机构申请,那么CA机构的工作量就会非常大。因此CA机构采取授权二级机构的方式来管理证书申请,经授权二级机构也可以签发服务器证书

各个顶级CA机构的根证书都内置在浏览器或者操作系统的发行包中,这些根证书当然可以使用来给客户签发证书请求,但实际中很少有CA机构这么干,原因在于如果某个根证书有问题,比如私钥泄露,基本上是灾难性的, 对应的CA根证书很快就会被各大浏览器和操作系统厂商移除,基本上不倒闭也去了半条命。所以绝大部分的顶级CA机构会用自己的根证书私钥签署一些二级(或多级)CA证书,然后用这些二级(或多级)CA证书的私钥给客户签发证书请求, 当某个二(多)级CA证书出幺蛾子的时候,只要把该二(多)级证书吊销就可以,波及面也不会那么大。

证书签发

  • 根证书CA机构 使用自己的私钥中间证书进行签名,授权中间机构证书;
  • 中间机构使用自己的私钥服务器证书进行签名,从而授权服务器证书

证书校验

  • 浏览器解析服务器证书的内容并提取关键信息
  • 检查服务器证书“Subject”的CN属性或者“Subject Alternative Name”的属性与网站是否匹配
  • 通过“Issuer”或者“Authority Key Identifier”属性查找签署证书的CA证书, “Issuer”属性是跟CA证书的“Subject”对应,“Authority Key Identifier”是跟CA证书的“Subject Key Identifier”相对应
  • 浏览器找到了签署证书的CA证书后,就用这个CA证书的公钥来验证服务器证书的签名

        a、用CA证书公钥解密服务器证书上的签名,得到服务器证书摘要信息;
        b、用摘要算法计算服务器证书摘要信息;
        c、然后对比两个摘要信息。

  • 循环以上步骤直到最后用来验证的CA证书是根证书,根证书也是自签名证书
  • 如果以上步骤都通过,并且根证书已经内置在浏览器或者操作系统中,则校验通过

中间证书怎么获取

这里可能大家有一个疑问,根证书是内置在终端设备上或浏览器中的,那中间机构证书怎么获取?这里以百度Tls证书进行举例,百度服务器证书 签发者公钥(中间机构公钥)通过下图中的URI获取:

 证书链总结

结合实际的使用场景对证书链进行一个归纳:

  1. 为了获取网站服务器的公钥,需要获取网站服务器的证书,因为公钥就保存在该证书中

  2. 为了证明获取到的网站服务器证书是可信的,就要看该证书是否被 intermediate 权威机构认证,等价于是否有权威机构的数字签名

  3. 有了权威机构的数字签名,而权威机构就是可信的吗?需要继续往上验证,即查看是否存在上一级权威认证机构的数字签名

  4. 信任链条的终点是Root CA,采用自签名,对他的签名只能无条件信任(操作系统、浏览器预置或自己下载安装)

6. https详细握手过程

为什么charles等抓包工具或者浏览器控制台看到的https返回是明文的?

首先说明一下,https其实相当于http+tsl,(tsl和ssl只是不同的版本称呼问题,暂且这么理解),大家都知道,http是明文操作,那也就是说所有的加密解密操作都在tls这边,具体关系可以参看下面这张图

浏览器是属于应用层之上的应用吧,所以浏览器控制台的输出都是已经经过tls解密过的。 那为什么charles等抓包工具看到的也是明文的呢?是不是也是因为charles是应用层之上的应用呢?不尽然如此!charles在抓包过程中是起到了中间代理的作用,浏览器=====》Charles======》服务器,charles相对于浏览器来说,是服务端,相对于服务端来时,是客户端。charles在抓取https的包时,是需要浏览器先安装并信任自己的证书的(相当于服务器的证书),具体可看下图

 所以,能看出来,之所以charles能看到明文,是因为你主动信任了他的证书

为什么制作数字签名时需要hash一次?

其实仔细想一想,如果制作签名的时候不做hash,有什么影响吗?其实还真没有影响,还是能正常运行,给证书明文签名和给明文的hash签名,不会给验证有任何影响。但为什么要做一次hash呢?还记得上篇文章上提到过,非对称加密是非常耗时和耗性能的,对一个字符加密和对100个字符加密哪个更快一些,掰着大拇指想一想也是越短加密越快,所以原因出来了,证书的明文基本都很长,但是经过hash之后都很短,而且基本都是固定的长度了,所以出于性能的考虑,在制作签名时需要hash一次

https的详细握手过程

https在七层协议里面属于应用层,他基于tcp协议,所以,https握手的过程,一定先经过tcp的三次握手,tcp链接建立好之后,才进入https的对称密钥协商过程,对称密钥协商好之后,就开始正常的收发数据流程。

接下来我就拿实际网络数据包来解释https的整个详细的握手过程

于是我打开wireshark抓包工具,并随手打开命令行,怯意的输入了如下一行命令

curl https://www.baidu.com

上面其实涉及到两个问题

  1. 为什么是wireshark,而不是fiddler 或者 charles

    fiddler 和charles主要是用于抓取应用层协议https/http等上层的应用数据,都是建立链接成功后的数据,而wireshark是可以抓取所有协议的数据包(直接读取网卡数据),我们的目的是抓取https建立链接成功前的过程,所以我们选择wireshark

  2. 为什么是用curl, 而不是在浏览器打开https://www.baidu.com

    curl是只发送一个请求,如果是用浏览器打开百度,那百度页面里面的各种资源也会发送请求,容易造成很多不必要的数据包

好,重点来了,开始上图:

 上面两张图就是用curl请求https://www.baidu.com用wireshark抓到的所有数据包

第一步

解释说明:tcp三次握手,这个不做解释,如果这块不清楚,比如ack,seq,mss,win都代表什么意思,这个可以去搜索相关tcp握手的文章

第二步:客户端发送client_hello

解释说明:客户端发送client_hello,包含以下内容(请自行对照上图) 1. 包含TLS版本信息 2. 随机数(用于后续的密钥协商)random_C 3. 加密套件候选列表 4. 压缩算法候选列表 5. 扩展字段 6. 其他 

第三步:服务端发送server_hello

解释说明:服务端收到客户端的client_hello之后,发送server_hello,并返回协商的信息结果 1. 选择使用的TLS协议版本 version 2. 选择的加密套件 cipher suite 3. 选择的压缩算法 compression method 4. 随机数 random_S 5. 其他

第四步:服务端发送证书

解释说明:服务端发送完server_hello后,紧接着开始发送自己的证书,从图可知:因包含证书的报文长度是3761,所以此报文在tcp这块做了分段,分了3个报文把证书发送完了 

分段(Segmentation)指发生在使用TCP协议的传输层中的数据切分行为(超过MSS)

分片(Fragmentation)指发生在使用IPv4协议的网络IP层中的数据切分行为(超过MTU)

最大报文段长度(Maximum Segment Size),即MSS,为TCP传输层的最大载荷上限(即应用层数据最大长度)

最大传输单元(Maximum Transmission Unit),即MTU,为数据链路层的最大载荷上限(即IP数据报最大长度)

MTU = MSS + TCP首部长度 + IP首部长度

故在以太网中(网络层以IPv4为例):

MSS = 以太网MTU - TCP首部长度 - IPv4首部长度 = 1500 - 20 - 20 = 1460字节

  1. (TCP)分段和(IP)分片各自发生在不同的协议层(分段-TCP传输层,分片-IP层)
  2. 切分的原因不尽相同(数据量超出上限,分段应用数据上限-MSS,分片上限-MTU)
  3. 虽然分段和分片不会在发送方同时发生,但却可能在同一次通信过程中分别在发送主机(分段)和转发设备(分片)中发生
  4. 发送端采用的传输层协议不同(分段-TCP,分片-UDP/ICMP…)
  5. 分段仅可能发生在发送端,分片不仅可能发生在发送端,更还可能发生在路径上任何一台工作在三层或以上的设备中,而两者的重组都只会发生在接收端

第五步:服务端发送Server Key Exchange

解释说明:对于使用DHE/ECDHE非对称密钥协商算法的SSL握手,将发送该类型握手。RSA算法不会进行该握手流程(DH、ECDH也不会发送server key exchange),也就是说此报文不一定要发送,视加密算法而定。SSL中的RSA、DHE、ECDHE、ECDH流程与区别可以参考

TLS/SSL 协议详解 (30) SSL中的RSA、DHE、ECDHE、ECDH流程与区别_Mrpre的博客-CSDN博客_dhe ecdhe

第六步:服务端发送Server Hello Done

解释说明:通知客户端 server_hello 信息发送结束

第七步:客户端发送.client_key_exchange+change_cipher_spec+encrypted_handshake_message

解释说明:

client端收到server发来的证书,会去验证证书,当认为证书可信之后,会向server发送:

  • client_key_exchange,消息中包含客户端这边的EC Diffie-Hellman算法相关参数,然后服务器和客户端都可根据接收到的对方参数和自身参数运算出Premaster secret,为生成会话密钥做准备。
  • change_cipher_spec,此时client端和server端都可以根据之前通信内容计算出Master Secret(加密传输所使用的对称加密秘钥),客户端通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信
  • encrypted_handshake_message,客户端使用之前握手过程中获得的服务器随机数、客户端随机数、Premaster secret计算生成会话密钥master secret,然后使用该会话密钥加密之前所有收发握手消息的Hash和MAC值,发送给服务器,以验证加密通信是否可用。服务器将使用相同的方法生成相同的会话密钥以解密此消息,校验其中的Hash和MAC值。

第八步:服务端发送New Session Ticket

 解释说明:服务器给客户端一个会话,用处就是在一段时间之内(超时时间到来之前),双方都以协商的密钥进行通信。

第九步:服务端发送change_cipher_spec

解释说明:服务端解密客户端发送的参数,然后按照同样的算法计算出协商密钥,并通过客户端发送的encrypted_handshake_message验证有效性,验证通过,发送该报文,告知客户端,以后可以拿协商的密钥来通信了

第十步:服务端发送encrypted_handshake_message

解释说明:目的同样是测试密钥的有效性,客户端发送该报文是为了验证服务端能正常解密,客户端能正常加密,相反:服务端发送该报文是为了验证客户端能正常解密,服务端能正常加密

第十一步:完成密钥协商,开始发送数据

解释说明:数据同样是分段发送的

第十二步:完成数据发送,4次tcp挥手

 解释说明:红框的意思是:客户端或服务器发送的,意味着加密通信因为某些原因需要中断,警告对方不要再发送敏感的数据,服务端数据发送完成也会有此数据包,可不关注

最后用一张图来说明以下过程

7. java中的https

jdk存放根证书和各大CA证书的位置

$JAVA_HOME/jre/lib/security/cacerts

查看方式(默认密码changeit):

keytool -list -v -keystore $JAVA_HOME/jre/lib/security/cacerts

httpClient进行https连接代码

第一步(用户代码)初始化httpClient:

// 使用jdk自带的trustManager
List<X509TrustManager> trustManagerList = new ArrayList<X509TrustManager>();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null);
for (TrustManager tm : tmf.getTrustManagers()) {
    trustManagerList.add((X509TrustManager)tm);
}

// 初始化ssl上下文
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagerList, null);

// 创建sslSocketFactory
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier());

// 注册http和https的处理工厂
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register(Protocol.HTTP.toString(), PlainConnectionSocketFactory.getSocketFactory())
                .register(Protocol.HTTPS.toString(), sslSocketFactory).build();

// 创建连接池管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

// 创建httpClient
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).setUserAgent("console").disableContentCompression().disableAutomaticRetries().build();

第二步 (用户代码)发送https请求

CloseableHttpResponse response = httpClient.execute(httpRequest, httpContext)

第三步,系统代码内部处理流程

1. CloseableHttpClient.execute
2. InternalHttpClient.doExecute
3. execchain.RedirectExec.execute
4. execchain.ProtocolExec.execute
5. execchain.MainClientExec.execute
6. execchain.MainClientExec.establishRoute
7. PoolingHttpClientConnectionManager.connect
8. DefaultHttpClientConnectionOperator.connect
9. SSLConnectionSocketFactory.connectSocket
10.SSLSocket#startHandshake //开始握手
11. sun.security.ssl.SSLSocketImpl#readRecord
12. sun.security.ssl.Handshaker#process_record
13. sun.security.ssl.ClientHandshaker#processMessage
14. sun.security.ssl.ClientHandshaker#serverCertificate
15. X509TrustManagerImpl#checkTrusted //验证证书
16. SSLConnectionSocketFactory.verifyHostname //验证hostName

部分内容转载自:刨根问底系列之https详细握手过程 - 掘金

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值