我们每天上网都使用http协议,我们每天编程也使用http协议,Spring Cloud的微服务使用Http template实现RPC调用,到处都是HTTP这四个字……
作为一名天天接触HTTP协议的程序员,不管是前端还是后端,怎能不了解HTTP协议呢?就连校招面试,HTTP协议也作为考官必点的一道开胃菜进行提问!
不管是面试还是开发,学好http协议都是必要的~
上期我详细讲解了HTTP协议的流程,这期我们来聊下一个话题,那就是HTTPs协议,为什么HTTPS协议能保证通讯的安全,他与HTTP协议的不同在哪里?这其中到底发生了什么呢,泡杯咖啡,听我给你讲~。
我们先看看百度百科怎么讲的。
HTTPS (全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性 [1] 。HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。这个系统提供了身份验证与加密通讯方法。它被广泛用于万维网上安全敏感的通讯,例如交易支付等方面 [2] 。
这里有很多名词,比如传输加密、身份认证、SSL。很显然,SSL被用来实现HTTPS的传输加密与身份认证,而这两个就保证了信息传输过程的安全性。
首先是前传,HTTP的知识要了解。
八千字长文详细图解:从输入URL到浏览器显示页面到底发生了什么?
下图是HTTP和https在协议栈结构上的不同,为了兼容http协议,https协议在进行tcp链接通讯的时候,引入了SSL(也叫TLS)协议,在TCP握手的时候,通过SSL协议的规范进行了一些报文交换,进而让双方的通讯能够保证安全。到底是怎样的操作,让他几个报文交换就能保证通讯安全呢?首先我们需要一些密码学的知识。
SSL(Secure Socket Layer,安全套接字层):1994年为 Netscape 所研发,SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。
TLS(Transport Layer Security,传输层安全):其前身是 SSL,它最初的几个版本(SSL 1.0、SSL 2.0、SSL 3.0)由网景公司开发,1999年从 3.1 开始被 IETF 标准化并改名,发展至今已经有 TLS 1.0、TLS 1.1、TLS 1.2 三个版本。SSL3.0和TLS1.0由于存在安全漏洞,已经很少被使用到。TLS 1.3 改动会比较大,目前还在草案阶段,目前使用最广泛的是TLS 1.1、TLS 1.2。
什么样的信息传输才叫安全?我们可以拆分为四个目标。
为了使我们的数据安全送达对方手中,我们需要保证数据的安全,如何保证安全呢?我们可以使用一些加密手段,对数据进行编码、加密,让数据变成别人看不懂的乱码。但是接收方却知道解密的手段,这样第三者获取不到我们的数据,只有对方才能进行解密,获取数据。
如何进行数据加密?我们需要了解一下基本的数据加密模型。
明文 X X X:我们要发送的数据
密文 Y Y Y:经过加密的数据
加密秘钥 K K K:比特串,通过秘钥,明文可以被加密为密文。
解密秘钥 K K K:比特串,通过秘钥,密文可以被解密为明文。
加密和解密秘钥可以一样,也可以不一样,取决于加密算法的不同。
在这里,加密和解密秘钥都是K,表示加密解密秘钥一样。
加密行为: Y = E k ( X ) Y=E_{k}{(X)} Y=Ek(X)
E是Encrypt,即加密的英文缩写。通过这个表示,我们很容易就明白,X通过加密算法E、秘钥K,加密得到密文Y。这个过程也可以叫编码。
解密行为: D k ( Y ) = D k ( E k ( X ) ) = X D_{k}(Y)=D_{k}(E_{k}(X))=X Dk(Y)=Dk(Ek(X))=X
D是Decrypt,即解密的英文缩写。通过这个表示,很容易看出,Y通过秘钥、解密算法进行解密,就可以还原出X来,解密相当于加密的一种逆运算。这个过程也可以叫解码。这样的话,就可以达成较为安全的通讯了。
除了秘钥解密,有没有其他办法呢?有,我们可以穷举秘钥或者通过复杂的数学技巧来破解密文,但是前者需要庞大到不可能实现的算力,后者需要天才的数学天赋。这样破解的难度就变的极大,因此就达到了我们加密的目的。但是如果中间人截获了秘钥,那他不仅可以伪装发送者,还可以伪装接收者,神不知鬼不觉的发动攻击,获取机密信息。
理解了基本加密的思路,让我们再来了解一下两类密码体制。
所谓对称密钥密码体制,即加密密钥与解密密钥是使用相同的密码体制。如图所示,通信的双方使用的就是对称密钥。刚才我们讲解的加密模型,就是对称秘钥密码体制。
使用对称秘钥密码体制的算法,双方就可以凭借秘钥(相同的)进行加密和解码啦。比如经典的DES加密算法,就可以进行加密和解密运算。
数据加密标准 DES(Data Encryption Standard)
DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。需要注意的是,在某些文献中,作为算法的DES称为数据加密算法(Data Encryption Algorithm,DEA),已与作为标准的DES区分开来。
通常,我们不需要自己发明算法,也不需要深究原理,这由密码学家和数学家负责,但是我们需要了解加密算法和加密的基本原理,以备不时之需,比如和第三方系统对接接口,又比如面试官让你聊聊常用的加密算法等场景,必须给他说道说道。
所谓公钥密码体制,即加密密钥与解密密钥不同的密码体制。
公钥密码体制的加密模型,可以这样表示:
加密秘钥Public Key:向公众公开,简称PK
解密秘钥Secret Key:自己保留,简称SK
加密算法E,解密算法D也是向公众公开的。
下面加密开始
这样就完成了一次秘密通信,除非秘钥SK泄露,否则这样的交流很难被破解。
PK和SK的加密解密可以视为一种逆运算,虽然例子是公钥加密私钥解密,但是私钥加密也可以用公钥解密。
但是公钥加密后不能用公钥解密,必须用私钥解密,这就保证了数据的安全性。
我们可以看到,这一对秘钥,只能让A->B进行多对一单向通讯,不能双向一对一通讯。、
A可以是多个人,因为公钥是公开的。但是B只有一个,因为私钥是保密的。
当然,我们可以让他们双向通讯,只要A也生成一对秘钥,然后依照上述流程再走一遍,就可以实现双向的通讯了。
缺陷:比起对称公钥体系,这种体系更安全,只能获取一方的秘钥单向通讯,但是加密解密计算开销较大,在某些场景下,并不适合使用。
除此之外,如果入侵者代理B发布了自己的公钥,则A使用入侵者的公钥进行加密,入侵者使用自己的私钥解密,获得明文,就可以实现中间人攻击了。仅靠A和B两方并不能保证绝对的安全,我们需要一个公正方来证明B的真实性。B首先需要向CA申请证书,并携带证书进行通信,证明自己合法性,公证方称为CA,此时就可以保证通信安全了。
在现实当中,为了证明合同的真实性,我们使用盖章的方法来保证合同是真实的,在数字世界当中,如何保证在网络中数据的真实性呢?我们需要使用一种数字签名,来保证数据的真实性,实现报文鉴别。
数字签名可以达成三个目标:
如何实现数字签名?方法有很多种。使用公钥密码体系就可以做到。
A想发送明文X,并对X进行数字签名。要进行签名,需要使用A的私钥进行加密。
X 签 名 = E S K A ( X ) X_{签名}=E_{SK_{A}}(X) X签名=ESKA(X)
然后报文发送到接收者B手中。B使用A的公钥进行解密。
X = D P K A ( X 签 名 ) X=D_{PK_{A}}(X_{签名}) X=DPKA(X签名)
由于私钥不会被A以外的人知道,因此,我们可以核实签名。
由于私钥不会被A以外的人知道,因此,除了A以外的人不可能有机会篡改报文。
由于私钥不会被A以外的人知道,但是PKA可以公布,因此可以邀请第三者进行公证。A不可抵赖。
当然,A不能是黑客,否则这就是中间人攻击。要解决这个问题,需要引入CA机构,对A颁发一个证书,证明A不是黑客,是合法的,然后A携带证书信息发送到B,B就可以通过证书(CA机构的背书)明白,A是合法的。
如果在数字签名基础上还想要进行秘密通讯,很简单,只需使用公钥密码体制的加密方法,再给签名套上一层秘密通讯的加密解密(公钥体系)流程,就可以秘密通讯了。
使用非对称加密算法,是非常消耗计算机性能的,若明文X长度非常长,那么一次加密就要耗费很多的CPU时间,这在某些情况下是不可接受的,必须寻找其他的报文鉴别方法。
使用哈希算法,可以实现高效的报文鉴别。
哈希算法,使用密码散列函数来对数据进行编码。该函数具有如下特点:
对于这种东西,一般只能采用暴力法破解,如暴力破解、彩虹表、字典攻击、词表重整攻击、概率上下文无关文法。但是只要多次加盐加密,这些方法都很难破解哈希算法加密。
为了确保没有中间人通过伪造X、使用相同的H计算出H(X)来伪造Message信息,需要对H(X)进行加密。由于H(X)长度恒定且不长,这时候,加密的效率就是可以保证的了!
设秘钥为K,这里使用对称加密算法,K的安全性问题可以另外解决(比如使用公钥体系加密方法),此时假设是可以保证的。
只要保证了入侵者不知道秘钥K,就没有办法伪造出报文鉴别码MAC,接收者解密后就无法得到正确的散列值,因此比较失败,报文鉴别成功。
HTTPS的通信流程,就是在HTTP基础上,加上了TLS层进行安全认证。
层级关系可以参考下图。TLS可以说是运输层协议,但是其实在应用层的程序也会集成一部分TLS的内容用于接口调用和通讯。
HTTP服务器与TLS之间有一个TLS 套接字通道,用来传递数据给TLS层。TLS层对数据进行包装加密等操作,并负责连接TCP进行报文交换(TLS层与目标端口(443)也有一个套接字通道,该通道为TCP真正交流端口,就像HTTP的端口80一样)。
TLS协议提供以下三个安全保障,利用之前我们学习的前置知识,很容易理解这几个保证到底是怎么实现的。
这里过程参考了几幅图拿下 HTTPS,感谢作者的抓包和解读。
TLS是基于TCP协议的,进行TLS之前,首先进行日常的三次握手协议。这里内容参考上一篇文章。
其中每一个「框」都是一个记录(record)。记录是 TLS 收发数据的基本单位,类似于 TCP 里的 segment。
多个记录可以组合成一个 TCP 包发送,所以通常经过「四个消息」就可以完成 TLS 握手,也就是需要 2个 RTT 的时延,然后就可以在安全的通信环境里发送 HTTP 报文,实现 HTTPS 协议。
首先,为了安全,我们需要密钥加密。兼顾效率和安全,TLS使用公钥体系与对称密钥体系混合的方法进行密钥的获取。首先使用公钥体系的方法,非对称加密获取一个对称的密钥,之后双方使用对称密钥进行交流。这样保证了对称密钥不会被入侵者利用,也保证加密解密效率足够高。
这个对称密钥,在握手中称为会话密钥。
客户端首先会发一个「Client Hello」消息,和服务器打招呼。具体内容如下。
消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random)**,这个随机数会被服务端保留,它是生成对称加密密钥的材料之一。
当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random)。接着,返回「Server Hello」消息,消息里面有服务器确认的 TLS 版本号,也给出了随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。
可以看到,服务端选择的密码套件是 “Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256”。这个密码套件看起来真让人头晕,好一大串,但是其实它是有固定格式和规范的。基本的形式是「密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法」, 一般 WITH 单词前面有两个单词,第一个单词是约定密钥交换的算法,第二个单词是约定证书的验证算法。比如刚才的密码套件的意思就是:
就前面这两个客户端和服务端相互「打招呼」的过程,客户端和服务端就已确认了 TLS 版本和使用的密码套件,而且你可能发现客户端和服务端都会各自生成一个随机数,并且还会把随机数传递给对方。那这个随机数有啥用呢?其实这两个随机数是后续作为生成「会话密钥」的条件,所谓的会话密钥就是数据传输时,所使用的对称加密密钥。然后,服务端为了证明自己的身份,会发送「Server Certificate」给客户端,这个消息里含有数字证书。
这证书里,就含有我们需要的公钥。
数字证书和 CA 机构
一个数字证书通常包含了:
- 公钥;
- 持有者信息;
- 证书认证机构(CA)的信息;
- CA 对这份文件的数字签名及使用的算法;
- 证书有效期;
- 还有一些其他额外信息;
数字证书的作用,是用来认证公钥持有者的身份,以防止第三方进行冒充。说简单些,为了让服务端的公钥被大家信任,服务端的证书都是由 CA (Certificate Authority,证书认证机构)签名的,CA 就是网络世界里的公安局、公证中心,具有极高的可信度,所以由它来给各个公钥签名,信任的一方签发的证书,那必然证书也是被信任的。之所以要签名,是因为签名的作用可以避免中间人在获取证书时对证书内容的篡改。
证书签发流程
- 先是对数字证书的相关信息来一个hash运算,得到散列值。
- 然后CA自己的私钥加密散列值,这个操作我们之前讲过,是CA进行了数字签名。
- 然后把签名附加在文件证书上,这样就形成了数字证书。
客户端需要验证数字证书,确保CA机构的合格性。这是验证的常规操作,我们之前也讲了。
- 首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
- 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密Certificate Signature 内容,得到一个 Hash 值 H2 ;
- 最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。
证书链
如图。我们申请的证书,一般是中间证书签发的。比如百度。但是浏览器通常只信任根证书。因此浏览器会发起申请,验证百度证书的可信性。然后继续重复直到根证书。
- 客户端收到 baidu.com 的证书后,发现这个证书的签发者不是根证书,就无法根据本地已有的根证书中的公钥去验证 baidu.com 证书是否可信。于是,客户端根据 baidu.com 证书中的签发者,找到该证书的颁发机构是 “GlobalSign Organization Validation CA - SHA256 - G2”,然后向 CA 请求该中间证书。
- 请求到证书后发现 “GlobalSign Organization Validation CA - SHA256 - G2” 证书是由 “GlobalSign Root CA” 签发的,由于 “GlobalSign Root CA” 没有再上级签发机构,说明它是根证书,也就是自签证书。应用软件会检查此证书有否已预载于根证书清单上,如果有,则可以利用根证书中的公钥去验证 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,如果发现验证通过,就认为该中间证书是可信的。
- “GlobalSign Organization Validation CA - SHA256 - G2” 证书被信任后,可以使用 “GlobalSign Organization Validation CA - SHA256 - G2” 证书中的公钥去验证 baidu.com 证书的可信性,如果验证通过,就可以信任 baidu.com 证书。
根证书一般内置于操作系统或者浏览器中,我们也可以手动添加证书。
通过这种链条一样的方式,最终浏览器就可以成功进行证书的认证了。
证书报文:
随后,服务端发了「Server Hello Done」消息,目的是告诉客户端,我已经把该给你的东西都给你了,本次打招呼完毕。
客户端验证完证书后,认为可信则继续往下走。接着,客户端就会生成一个新的随机数 (pre-master),用服务器的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端。
服务端收到后,用 RSA 私钥解密,得到客户端发来的随机数 (pre-master)。至此,客户端和服务端双方都共享了三个随机数,分别是 Client Random、Server Random、pre-master。于是,双方根据已经得到的三个随机数,生成会话密钥(Master Secret),它是对称密钥,用于对后续的 HTTP 请求/响应的数据加解密。生成完会话密钥后,然后客户端发一个「Change Cipher Spec」,告诉服务端开始使用加密方式发送消息。
然后,客户端再发一个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信是否可用和之前握手信息是否有被中途篡改过。
可以发现,「Change Cipher Spec」之前传输的 TLS 握手数据都是明文,之后都是对称密钥加密的密文。
服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。
最后,就用「会话密钥」加解密 HTTP 请求和响应了。
可以看到,通过在CA证书里集成公钥,我们解决了中间人攻击的问题。
而私钥我们默认服务器应该安全的保管,责任在接收方,因此是安全的。
若入侵者破解了私钥,过去被第三方截获的所有 TLS 通讯密文都会被破解。因此RSA也不是百分百安全的,为了避免这个问题,可以使用 安全的DH 密钥协商算法、效率更好的ECDHE 密钥协商算法(现在一般都用这个),但是在这里就不展开讲了。可以自己百度看看。
这里的过程比较繁杂,可以搭配下面的流程图,配合抓包内容进行学习。
八千字长文详细图解:从输入URL到浏览器显示页面到底发生了什么?