公众号:程序员白特,欢迎一起交流学习~
HTTPS是什么
HTTP为什么不安全?
https被认为是通信安全的http,除了http多了s和默认端口改成了443之外,其他都是沿用的http(除了明文和不安全),最主要的改变就是http是over tcp,而https是 https over ssl over tcp。安全的特性都是ssl做的
通信安全的四个特征
通信安全有四个特征:
- 机密性:数据不能泄露,不能被代理之类的窃取到破解
- 完整性:数据不能被篡改,机密性只保证不能泄露,完整性要求保证数据不能被修改
- 身份认证:能够证明我是我,发到正确的客户端或者服务器
- 不可否认:也就是_不可抵赖_你发了就不能承认没发,同样没发也不可能抵赖发了
SSL介绍
https相对http的改动主要是下层多了一个ssltls层,加密的功能就是这层实现的。
ssl是会话层协议,是信息安全领域里面的权威标准。由网景公司研发,发展到v3的时候改名为了tls。三个版本v1.1,v1.2,v1.3。
现在用的大部分都是v1.2,主要分析的也是它。
使用tls建立链接需要_选定一个加密套件进行安全通信*,同样*需要两端协商所以客户端会带上自己支持哪些加密套件clientsuites字段表示这些套件,serversuite是最终协商采用的加密套件。_
- ssl是权威标准,比较知名的实现是openssl(开源密码学工具包),一般nginx都是在openssl的基础上实现tls的功能
SSL加密
对称加密和非对称加密介绍,特点
tsl中使用的加密解密算法分为对称和非对称加密
一般_对称加密算法简单加密速度快*(加密解密都是用同一个钥也叫公钥)但是*公钥的传输不安全,公钥是通过明文在网络传输给客户端的_,链接的时候都是先服务端给客户端公钥,然后客户端发消息用这个公钥加密 但是如果公钥传输明文被黑客替换后就会把你的数据给到黑客(替换destnation和使用的加密算法)
另外一个是非对称,加密算法复杂所以加密速度很慢,真正使用这种网络通信会很慢。
为什么说非对称更加安全呢?因为 公钥是公开的,私钥只能服务器持有,私钥加密了公钥之后传输给客户端,客户端用公钥解密这个私钥加密的公钥以后就用这个公钥进行对称加密传输数据。
两种加密协议结合使用保障通信安全
- 公钥加密私钥解密,私钥签名公钥验签
公钥加密私钥解密,私钥签名公钥验签(这里的私钥就是加密公钥的,确认你发的这个是服务器发的不可抵赖,这也是安全里面的一个特性签名了就代表你是你不能抵赖因为私钥只有你有,哪怕黑客获取了响应消息也不能伪造因为要进行私钥签名他没有服务器的私钥所以不会成功)
- 客户端也需要数字签名(私钥加密保证签名是自己保证不可抵赖,私钥加密摘要保证完整性)
黑客也可以伪装成客户端给服务端消息因此同样需要数字签名验证客户端的身份,签名的用法就是私钥加密。
https加密协议的大致流程
目前总结下流程 就是
- 客户端用非对称自己的私钥加密摘要然后发给服务端
- 服务端用客户端公布的公钥解密后,再利用自己的非对称私钥 加密 之后 会话对称加密用的公钥
- 然后_客户端用服务端公布的公钥_ 解开 拿到对称加密的公钥。(保障机密性和身份认证也就是不可抵赖)
- 之后***客户端用服务端对称加密的公钥传输信息,** *用_自己的私钥 非对称加密消息摘要*,服务端用_对称加密的秘钥 解开信息然后 再用客户端公布的公钥 解开摘要验证内容是否完整_。(*数字摘要用非对称加密来保障 数据的完整性_)
已经有了机密性和身份认证来保证数据安全, 为什么还需要数字签名来验证数据的完整性?
为什么有了 非对称加密的私钥签名还需要完整性?
假设我们使用非对称加密的签名机制来进行数据传输,其中A和B之间需要进行敏感信息的交换。A使用自己的私钥对信息进行签名,然后将签名后的信息和原始信息一起发送给B。B收到信息后,使用A的公钥来验证签名,并确保原始信息没有被篡改。
然而,这个过程中可能存在一些问题。首先,*密钥管理和分发是一个重要的问题。 *A和B需要***保证彼此的密钥交换是安全可靠的,这就需要进行安全的密钥协商和分发。** *如果_密钥被泄露或更改,那么签名机制就会失效,数据的安全性就无法得到保障。_
通过_引入完整性校验,我们可以增加一层保障,确保数据在传输过程中不会被篡改。_
即使_出现了一些技术问题或者密钥被泄露等情况,完整性校验也可以检测到数据的变化,从而提醒我们及时采取措施保护数据的完整性。_
也就是说 使用密钥虽然可以保证数据不被篡改,但是 如果使用的秘钥被篡改,那么数据就会被泄露和篡改,通过使用完整性校验,可以多一层保证 ,但是如果数字摘要的 加密密钥 被泄露和篡改 依然会有问题。只是说可以多一层保证。
- 摘要算法
这里的_完整性就是通过摘要算法(大内容变成小内容,小内容推导不出来大内容)完成_的,普遍的有md5和sha1,但是tls采用的是sha2,摘要算法同样需要加密(这个加密用对称同样不安全,所以也需要非对称下面具体讲)
CA证书和ca机构的出现
可以看到目前通信已经算安全了,但是唯一一个不安全的地方就是_公钥的信任问题*。黑客也可以进行发布自己的公钥,*如果服务器和客户端拿到的是黑客的公钥那么通信就不安全了。(黑客可以解密得到所有通信数据)_
具体来说 就是_如何证明你这个公钥是你的*?这和如何证明这是你发的消息一样了不是?所以也*需要私钥对这个公布的公钥加密_,那***如何保证这个私钥解密的公钥就是某个人的呢?同样也需要私钥进行加密,** *所以就陷入了圈之中。
也就是公钥 要进行 验签的时候,如果保证这个公钥是 要签名人的公钥,答案是在上一层的私钥进行签名,同样这个公钥如何证明是上一层的,就需要上上一层,一直往上面寻找这个公钥就是某个权威机构的公钥,你用这个就能找到下面所有验证过的签名公钥信息。(防不胜防,权威机构也可能被攻击和劫持)
所以根据这个思路 出现了ca机构和ca证书。
你不是一直需要往上验证是谁发布的吗?也就是验证谁签名的。
CA机构的级别:dv,ov,ev
全世界的ca就那几家分别签发的证书是dv,ov,ev三中。
证书中会包含一些信息然后用上一层的证书验证这个证书是否是安全的,一直往上找直到找到根证书验证包含。
dv是域名级别可信,可信度最低;ev是最高的走了法律程序的证书。
根ca证书如何保存
这些根证书该如何保存呢,很简单保存在操作系统里面,拿到客户端或者服务端给过来的证书(以前是公钥现在是证书 代表的信息会更多),然后通过操作系统的这些根证书验证是否是安全的,如果没有包含这个网站的公钥那么这个传递过来的证书就可能是假的不安全的通信。
可能是优点也有可能是缺点。*有些没有备案交给ca验证的公钥证书信息在通信的时候会被拦截提示不安全;只有经过备案的公钥信息才可以访问安全; *或者可以_在自己的操作系统内部添加这个根证书的信息_
必须要向ca备案,不然找不到你和服务器通信使用的公钥是否被黑客劫持,用的是黑客的公钥,你的所有通信内容黑客都可以看到,为了方便备案很多地方级的ca系统 是免费的直接就可以备案添加到证书系统里面(迁移https需要用到的知识)
为什么出现这么多安全措施?有必要吗?
这个世界不是绝对安全的,通信也同样如此。
如同非对称加密出现后为什么还需要完整性一样,世界上没有绝对安全,非对称加密的秘钥可能破解然后黑客修改其中包含的公钥盗取信息; 同样_ca机构一样会被欺骗 签发了一个错误的证书不安全的证书,具体就是黑客可以攻破ca然后把自己的证书加进去从而让自己的公钥也是安全的_。
这些事情在历史上真的发生过!这种情况只能ca打补丁更新修复或者清空操作系统里的ca证书加入黑名单
TSL的完整通信过程:加入加密协议和CA证书后
这里总结下 https下层的tls是如何交换密码套间和使用的公钥的。
第一张图是目前的https的tls握手流程。
- 首先_客户端向服务端发送自己_ 支持哪些密码套件和客户端使用的tls版本 *, *在带上一个_客户端的随机数(clientrandom)_ ****随着clienthello发出
- _服务端收到密码套间和客户端使用的tls版本_后将 确定要使用的tls版本和密码套间 ******还有一个 服务端的随机数(serverrandom) ****发送给客户端
- 接着_服务端给客户端_ 发送服务端的ca证书 ,在之后利用ecdhe算法(服务端决定采用的加密套件)算_出serverparam_ (服务器计算出来的公钥) ****并用 服务端私钥加密进行签名代表是服务器发送的serverparam。
ECDHE算法:该加密协议 可以让服务器和客户端算出自己使用的公钥(服务端算出serverparam,客户端算出clientparam都是自己算出来的公钥信息
- 客户端收到后验证ca证书链判断ca是否是真的。接着_通过_ echde算出客户端的公钥(clientparam) ****。undefined接着再_利用_ echdE算法通过serverparam,clientparam 算出 premaster *。 *有了_premaster,clientrandom,severrandom后生成加密秘钥的主密钥mastersecret*。undefined客户端*把自己的client param发送给服务端_
severparam是服务端私钥加密过的公钥且通过了ca检查,clientparam也是用echde算出来的客户端公钥,客户端_算出pre之后再算出master*,然后*再用master生成客户端的公钥_ 以后给服务端发消息就用这个公钥进行对称加密通信。
- 客户端给服务端发送clientparam后,服务端也是一样的操作,算出pre,在算出master再算出服务端用的公钥 以后就 用这个来 加密之后 发给客户端的数据。
- 双端反馈各自算出公钥后,就会进行_把前面的内容加密用算出的公钥加密然后用各自的非对称私钥加密计算出的摘要_ 来让对方 验证是否正确。
不管怎样玩,最后 对内容的加密都是使用的 对称加密(数据量过大,使用对称加密快),但是_对于数字摘要来说使用的都是非对称加密_(数量少且更加安全。、)。
- 双方反馈都正确后就可以 之后的加密传输了
- 这时候才开始http的发送(客户端发送请求头等等信息)
可以看到加密了很多次,首先_公钥和公钥利用echde算出个预备的秘钥_ 接着再 利用客户端服务端的随机数生产master,三个随机数更加安全,然后_再生成各自端用的公钥进行之后的加密发送_
传统的没有这么复杂,pre是直接随机数算出来的(第二张图)而不是上面的利用加密算法套件对两个公钥进行再次加密得出。但是之后的流程是一样的。利用客户端服务端随机数和pre删除mastersecret
HTTPS的优化:多了TLS的连接建立,如何确保和http一样快
- 硬件优化:升级cpu但需要花钱
- 软件优化:性价比高
软件内核升级
需要所有应用做适配工作量太大
协议升级
*tls1.2需要链接两次才能让客户端和服务端各自计算出mastersecret。 *升级为tls1.3后 在_clienthello阶段就可以把clientparam和clientrandom发给服务端计算出mastersecret_中使用的服务端公钥(cp和sp算出pre,ct和st和pre算出master),然后服务端再把st和sp给到客户端,客户端计算使用的公钥。减少了一次通信rtp
CA证书链验证优化:crl,ocsp的出现
由于_服务端会把证书链发给客户端,然后客户端进行证书的链条验证*,其中客户端不仅需要用传递过来的sp公钥解密出ca证书还需要*去不断的访问ca看当前证书是否还有效_
访问ca又是发起连接通信的过程 耗时可想而知
- CRL:之后出现了crl,crl是 定期更新的被撤销认证的证书序号,只要能拿到这些撤销了的序号就不用每次都访问ca进行通信验证了。
但是crl是定期更新存在时间窗口问题,而且随着吊销的证书不断增多传输的数据量也会变大,而且如果请求的crl里面只有一个或者甚至没有ca链中的过期证书那么就是完全浪费了时间和流量。
- OCSP:OCSP Stapling_将检查SSL证书吊销状态的过程转移到了Web服务器端,而不是客户端*,通过Web服务器(如Nginx)定期从CA获取SSL证书的OCSP响应并存储在内存中。然后在*与客户端建立SSL连接过程中,Web服务器会一并返回已缓存的OCSP响应给客户端。_
关于OCSP的讲解链接:
www.php.cn/faq/557367.…
不管怎样,ca证书验证的过程最少需要一次网络请求,这是不可避免的
会话复用
通过前面的讲解可以看出_https就是比http多了一次tls的握手*,这次握手*主要是客户端服务端协商计算出master secret用于会话的加密通信_。重点就是计算出master之后的钥匙用于加密通信
如果_能够对这个master secret重用_那么就不需要tls那么复杂的计算过程了。
因此_会话复用是指 客户端和服务端都保存一个会话id,并在内存中保存这次会话的master secret信息_。
当进行连接时,客户端发送会话id然后_服务端通过这个会话id找到服务端对应的mastersecret并对服务端使用的会话id,tls版本等进行加密发送给客户端进行验证是否可以重用*,客户端如果能够用对应会话id的秘钥解密拿到服务端使用的tls版本和服务端的会话id,如果_验证通过后会_ 发送给客户端一个_用master secret加密后的finish消息_代表*重用master secret。_
这种方式叫做sessionid复用,里面存储着会话id和对应的master secret。流程如图一
会话票证(session ticket)PSK
sessionid是_目前优化tls建连中使用最广泛的技术*,但是有个缺点是*sessionID需要客户端服务端都需要做保存,客户端没事但是对于服务端来说 成千上万个客户端访问就有成千上万个sessionid在服务器内存中占用,非常影响性能。_
痛点是服务器需要存储sessionid,突破口就是_将存储变为固定的一个算法计算,不需要存储只需要到时候服务器计算出来之后给客户端*(这个计算出来的东西就是_session ticket),客户端以后再次建连时带上这个ticket_, 服务端由于没有存储所以需要*根据一些客户端传送过来的数据借助算法得出这个ticket是否正确,是都可以进行复用链接_(比如tcp有个连接有效期时间,在有效期之后会关闭链接因此需要查看这个会话是否过期断连))ticket在服务端传送给客户端的过程中需要加密,这个加密的秘钥同样需要定期更新。
上面的sessionid和session ticket都需要一次rtt(往返),有没有0rtt呢?当然可以就是_发送ticket的时候把之后发送的数据也带上_ (是不是也算0 rtt呢?哈哈当然算因为_已经开始数据了_),这个方案叫做psk(pre shared key)就是客户端发送ticket的时候带上应用数据。
tls1.3里面废除了id和ticket,跳过tls证书验证 秘钥计算的方案采用的是psk(ticket上的优化)
HTTP2是什么?
http2的优化
http1.0的https已经是_足够安全的了但是对于数据传输并没有效率提升*,因此*官方在谷歌的协议推出之后又自己出了新的协议 :http2_(没有小数是因为以后没有小版本了)
http的优化点有哪些呢?
对header采用hpack的压缩算法进行压缩数据大小
之前的1.0只用了contentencoding的gzip压缩了body的数据但是大部分情况下body只有十几个字节而header则是有几百上千个字节,因此对header也进行了数据的压缩。
hpack里面通过字典来减少重复的值出现用索引进行代替。
全面采用二进制并将消息打散为frame帧传输
- 由于使用的是明文,会出现很多大小写,转义,中英文等等很多很多问题且占用空间大,http2的数据使用二进制后会更利好于tcp层。同样明文传输的很多问题也会得到解决。
- 以往的header和body现在被打散为了frame传输(没有结构了,而是零碎的frame),tcp层也是用frame传输数据的,向tcp靠拢,分为header frane,body frame(如下图)
http2中frame的数据结构
- 首先是_header frame*,第一个是这个_数据帧的总长度,然后是帧类型_(数据帧or控制帧data和header都属于数据帧类型),*标志位,标识符_(流id,通过流id就可以重组乱序的流为有顺序的流了,首先一个流里面的数据帧是有严格顺序的,再根据数据帧的流id就可以还原出有顺序的流了)
- 在接着就是_data frame_
虚拟的流基本解决了“队头阻塞”问题:数据帧传输共享虚拟数据流通道(多路复用)
以往的http1.0是客户端发送完后_需要等待服务端返回才能再次发送*,如果*前一个消息处理耗时那么就会影响后面的消息发送,_ ****造成“对头阻塞”问题。
在http2中这个问题通过另外一种方式解决了,数据帧的传输依赖于同一个消息,同一个消息的多个数据帧是有序有标识的,在通信中建立了流的概念。流用于传输数据帧,在发送到服务端后在进行组装成http1.0的body和head。对于流来说并不关心数据帧的内容,因此可以实现多路复用,一个流上可以传输多个不同消息的数据帧,等到了服务端在进行组装(同时也会出现多个控制帧用于控制流的传输)如何解决对头阻塞?由于不在是发送以前的消息而是接住流发送数据,所以依赖的东西由前一个消息的响应变成了 是否有流通道空闲传输数据。
关于流的详细解释
一个流里面的数据帧在发送流的时候必须保证是有序的,但是流的发送是无序且可以并发多个发送的,客户端可以借助流id将无序的流变为有顺序的流发给服务端
虽然多个流发送的时候没有先后顺序 可以无序发送但是流可以设置优先级先发送哪个流。
总结:流之间没有先后顺序但是可以有优先级,一个流里面发送的数据帧必须保证是顺序的(但是不需要保证多个流按顺序发送,因此,*流的id不能重用(客户端奇数服务端偶数) *,客户端服务端都可以发起流的传输,流可以并发发送。
主流浏览器强制使用http2的加密版本(h2c代表使用加密,h2不使用加密)
为了兼容1.0,数据依然是明文传输只不过最后会转为二进制。
http2的加密版本大部分浏览器使用的都是这个,出现了后缀h2c和h2版本
HTTP2的缺点
不允许对一个域名建立多个tcp链接只能有一个tcp链接
http1.1可以对一个域名最多建立6-8个tcp链接,一是队头阻塞二是在下载文件时可以使用这种方式实现并行下载
(一个下载一个tcp链接但是1.1要求请求只能在上一个请求的响应回来后才能发送)开多个链接就可以间接并发发送多个请求了
由于http2有了stream的概念,stream可以 并发发送无需等待服务器相应消息
(基本解决了对头阻塞 也解决了 大文件下载不需要等待服务器相应消息 因此可以一直发送请求 和1.1的开多个tcp链接_目的一样都是为了可以一直发送请求_ 不需要等待 接受 相应的响应消息 )又可以实现双向传输。
因此http1里面的对一个域名建立多个链接获取数据解决阻塞的方式也就不需要了(发送请求消息得等到上一个请求的响应消息发送,因此需要多个链接发送多个请求)
因此_http2里面由于这个特性禁用了 对一个域名建立多个链接*,因为你可以*用stream 双向通信还不需要等待服务器相应就可以发送消息还可以并发发送多个流(流直接互不干扰)_
但是这种方式也有缺点就是由于不需要响应就可以发送请求, 黑客可以并发发送多个请求对服务器进行攻击。
http1.0,https,http2的协议栈区别
http2在1.0的基础上新增了二进制帧序列frame,和strme流的概念,并且内置了tls等https的特性
HTTP3是什么?
HTTP3的优化
回顾对头阻塞问题:
http2解决了大部分情况下1.0的队头阻塞问题,因为1.0发送请求消息后需要等待服务器发送相应消息后才能继续发送,如果前一个消息请求没有得到服务器的响应那么后面的消息都不会进行发送,但是http2多个消息直接变成了多个流的形式,流直接可以并发发送没有顺序,因此消息转换为流之后没有了依赖关系可以一直发送无需等待响应 且可以进行多路复用。
彻底解决“队头阻塞”问题
为什么http2解决的是大部分的队头阻塞问题?
因为http是上层协议,其依赖的_tcp底层协议也有队头阻塞问题*,tcp里面有一个丢包重传机制,对于tcp协议来说会把流拆分为更小的segemant段传输,*当前面的包没有接受到时即使后面的包传输过来了也不会进行给上次而是放在缓存区中等待前面的包传输过来之后再组装发给上层。_
由于_tcp是可靠传输对于前面丢失的包只能阻塞等待,直到前面的包都接受到之后再给上层,不允许丢包。_
http3的改进(over quic)
可以看到tcp存在队头阻塞问题想要彻底解决就不能使用tcp了,谷歌看到了这个问题于是推出了gquic协议(集tls,stream,pack等于一身的应用层协议),后来 ietf标准化对齐进行了提取成了iquic传输层协议 (和tcpudp一层)。
QUIC定义:
quic对udp进行了封装处理让其可以完美解决队头阻塞对于丢失的包不关系可以继续传输,且由于内置了stream和tls等,http3协议实现很简单,大部分工作都是quic完成的。
以后quic都是指的iquic协议
http3基于两个端点的随机id作为通信双方地址保持链接 而不是以前的ip
http2以前基于tcp链接其是通过ip地址来判断通信地址是否发生变化的,
如果ip地址变化那么就需要重新建立tcp链接,但由于http3是基于随机id因此不会进行重连
http3如何建立链接呢?
http3没有像1.0和2那样指定服务器的端口号,原因是需要先通过http2进行链接然后通过http2里面的扩展帧来进行使用http3进行链接