上篇主要是介绍了HTTP存在的两大安全问题web
明文算法
没法验证服务器的真实性chrome
从而引出了TLS。本篇就来着重介绍下TLS。浏览器
提及TLS可能有些人还比较陌生,但若是说到SSL,那知道的人就更多了。TLS其实就是SSL发展而来,版本演进大致为SSL 2.0 -> SSL 3.0 -> TLS 1.0(能够看作是SSL 3.1版)。缓存
TLS主要提供三个基本服务安全
加密服务器
身份验证cookie
消息完整性校验网络
其中第三点是网络协议中经常使用的一个校验和机制,和咱们这边要说的安全话题不是太相关,再也不赘述。而前两点正好是对应了HTTP的两个问题,下面分别介绍下。session
其实网络通讯能够类比为人之间的对话,好比A和B之间须要沟通,但两人无法直接见面,但有一个传话人能够代为传话。这样两人就能间接沟通了,只不过这个传话的过程是有必定风险(被记录,被篡改)的。
为了不这种风险,长此以往A和B想出了一个办法:他们约定使用传话人不会的上海话沟通。这样每次传话人都听不懂啥意思,只能尽量有样学样地把A说给他的话学给B听。
这个普通话翻译成上海话的过程,对于传话人来讲就是一种内容的『加密』。但这个方法不必定是惟一的,可能之后另外一个传话人懂上海话呢?因而在每次使用方言沟通以前,A和B会先行沟通下他们此次对话使用的语种(两人真是牛逼!)
这个选择语种的过程,其实就是两人之间关于翻译方式的『协商』过程。传话人要想从中捣鬼,就能立马被发现,这样AB两人为了沟通安全就会中止此次沟通,转而找其余的传话人传话了。
以上看到AB两人机智的采用『翻译』『协商』两招完成的安全沟通了。将一样的方法拿到网络通讯中,就是TLS的加密机制了。
这个图描述了发送端和接收端是怎么协商他们之间的加密机制的。TLS协议是基于TCP协议之上的,图中第一个蓝色往返是TCP的握手过程,不在本文范畴中按下不表。以后的两次橙色的往返,就是咱们要重点说的协商过程了,能够叫作TLS的握手。握手过程以下:
Client1:TLS版本号 + 所支持加密套件列表 + 但愿使用的TLS选项
Server1:选择一个客户端的加密套件 + 本身的公钥 + 本身的证书 + 但愿使用的TLS选项 +(要求客户端证书)
Client2:(本身的证书) + 使用服务器公钥和协商的加密套件加密一个对称密钥
Server2:使用私钥解密出对称密钥后,发送的加密的Finish消息,代表完成握手
咱们一步一步来仔细看。
第一步,客户说明他支持的全部加密套件(就像上海话、广东话同样)让服务端选择一个。这里和Server1还完成了一个TLS选项的协商,这能够理解为在肯定核心的加密方式的同时,顺便沟通下是否能支持一些『附加功能』。这些附加功能先按下不表,后续会介绍两个经常使用的。
第二步,服务端选择了加密套件。加密算法都是须要一个秘钥的,服务端这里给出的是本身的公钥。这里的公钥其实蕴含着一个关键点:秘钥协商过程采用了不对称加密。简单来讲,通常的对称加密以下
encrypt(明文,秘钥) = 密文
decrypt(密文,秘钥) = 明文
也就是说加密和解密用的是一个秘钥。而非对称加密就比较神奇了
encrypt(明文,公钥) = 密文
decrypt(密文,私钥) = 明文
加密和解密是须要不一样的密钥的。在TLS握手前,客户端自己不知道服务器的秘钥的,若是运用对称加密,那么服务端告诉客户端秘钥的过程当中,二者之间的中间节点其实也拿到秘钥了,这样后续二者之间的加密通讯,对于中间节点来讲也能解密出内容了。所以这里采用不对称加密,服务端给出一个公钥供客户端后续使用。此外这一步服务端还提供了证书,而且可能要求客户端提供证书。关于证书下文会提到,只要有了证书,就能保证和你通讯的对方是真实的,而不是别人伪造的。这里只要先理解,客户端看到服务端证书后,就可以相信服务端并使用服务端给的公钥了。
第三步,客户端给出了双方后续加密通讯所要使用的对称密钥,经过服务器公钥加密后返回给服务端。因为对称密钥是被不对称加密算法加密起来的,所以中间节点拿到后,没有办法解密出真正的内容。
第四步,服务端拿公钥对应的私钥解密出真正的对称密钥,至此加密算法和加密所需的密钥都肯定,服务端给客户端发送一个finish完成握手,证实双方都已经准备好加密通讯了。以后双方的交互均可以使用对称加密算法加密了。
TLS扩展
说完了如何肯定加密的算法和密钥后,咱们再说说那所谓的『附加功能』。所谓TLS的扩展,就是在TLS协议核心保持不变的基础上,对其进行扩展以使其支持更多的特性,这里简单介绍下两个扩展:ALPN和SNI。
ALPN(Application Layer Protocol Negotiation)即应用层协议协商,旨在将原来应用层协议中的协议协商,提早合并到TLS握手时完成,减小一次往返时间。这里经过一个例子来讲明下什么叫作应用层的协议协商。假设TLS握手完成了,接下来客户端和服务端就应该开始进行正常的HTTP通讯了,可是客户端认为HTTP协议比较慢(还记得上篇提到的流式传输的效率问题吗),在请求内容以前先向服务端提出『咱们能不能换HTTP2.0通讯』。服务端会根据本身支持HTTP2.0的状况给出答复,以后客户端会根据服务端的答复,使用HTTP或HTTP2.0向服务端请求内容。能够看到这个过程当中又多了一次协商升级协议的往返,致使客户端拿到返回结果的延迟更大了。为了优化这一点,在HTTP使用TLS加密的时候,会使用ALPN扩展,把协商升级协议的请求放在Client1中,服务端在Server1就返回说是否能够升级协议。这样TLS握手以后,客户端就能够直接按HTTP2.0发起请求而没必要多一次往返了。
SNI(Server Name Indication)叫作服务器名称指示,主要是支持服务端同一个机器部署有多个站点的状况的。前面说的证书、公钥私钥,都是针对某个站点来讲的。网站A和网站B可能租了同一台服务器,但他们的证书和密钥必定是不一样的。当客户端请求到这台公用服务器时,服务器也不知道该返回哪一个站点的信息了。这个时候使用SNI扩展,在client1带上访问站点的信息,服务端看到后,就会去找对应站点的证书、密钥了。
TLS的代价
任何事物都是有利有弊,引入TLS机制当然是可以保证安全,但却在TCP握手和HTTP通讯之间,多加了两个往返的TLS握手过程。特别是现代网页应用中充满了AJAX请求的场景下,请求的每每都是一个很小的资源,在一波TCP包中就能返回的。这种状况下握手须要三次往返,而真正有意义的请求往返只有一次,安全的代价很是大!(关于延迟对于性能的重要性,怎么强调都不为过,但每每会被人所忽视。这里不展开,但能够仔细思考下TCP的拥塞避免策略:在大延迟的状况下TCP包须要等上一波TCP包的往返后才会继续发送。这就是为何不少人都是50兆100兆的宽带还常常以为上网慢的缘由,由于延迟大!而运营商是历来不会宣传或保证延迟这个指标的)
固然,仍是有优化的办法的。上篇说过,现代web应用常常是打开一个网页同时会发送几十个请求。TLS握手这个协商加密套件和密钥的过程,须要重复几十次吗?固然不用。既然这几十个请求都是请求的同一个网站,那共用第一次请求的协商结果就行了。这样虽然第一次请求中TLS握手的代价很大(好比占了50%),但从整个网页全局看来,代价就下降到了百分之几了。目前一些主流浏览器的实现也确实利用了这个机制:他们会刻意先发起一个请求,待该请求的TLS连接完成后,再并发发起其余的请求。后续并发的请求就可以利用TLS协商后的结果了。
这种对协商结果的复用叫作TLS的『会话恢复机制』,主要有两种方案:Session Identifier 和 Session Ticket。
Session Identifier即会话标识符,是一种服务端保存会话信息的方案。
服务端为每一个客户端保存会话ID和协商后的会话参数
会话ID经过Server1带给客户端
客户端在后续建立链接的Client1中带上会话ID
若是两方都还记得会话ID,则可开启『简短握手』,一次往返便可
如该会话ID的信息在服务端不存在,则继续正常握手的第二个往返
方案总体思路其实和web应用的session相似,都是在服务端维护信息,客户端提供一个sessionId。所以该方案面临和session同样的挑战:服务端须要管理海量会话信息而且制定适当的淘汰策略。
Session Ticket即会话记录单,是一种客户端保存会话信息的方案。
协商成功后会话信息由服务器经过其私钥加密后携带在最后一次交换中
由客户端保存这个加密后的会话信息,但其没法解密得知具体内容
下次链接时客户端将其带在Client1的SessionTicket扩展信息中
这个方案的总体思路就和cookie相似了,有效地减轻了服务端的压力。但这属于一种较新的机制,目前还不是全部客户端都支持。
上面这么大篇幅都是介绍的TLS如何完成『加密』这个事情,然而以前说到的HTTP另外一个问题『没法验证服务器的真实性』仍是没有获得解决。毕竟若是有个节点要假装成服务器,和用户进行TLS的加解密交互,也是彻底没问题的吧。所以,TLS中还须要经过『证书机制』来保证你所访问的服务器是真实的。从安全层面来讲,『证书』和『加密』是同等重要的两个机制。
作个假设,你和小A是朋友,大家之间什么事均可以畅所欲言。大家当面聊天时,你不介意把本身的隐私和小A分享。你的分享,实际上是创建在『当面』的基础上的,也就是你经过『辨识小A的外貌』来判定他的真实性后,才会信任他与他分享。若是有一天小A经过一个陌生的电话联系你,那你恐怕就不会这么毫无防备了。为了验证电话那头真的是小A,大家之间可能须要一些验证机制,好比让小A说说大家小时候发生的某个事或者在大家两个面对面的时候肯定个暗号。这个事件或者暗号,其实就是你用来认证小A的『证书』了。 这样你和小A之间的认证就OK了。可有一天,一个自称小A朋友的小B找到你,想叫你帮个忙,能够你以前历来没据说太小B。这个时候为了验证小B是不是可信的,你只能找来小A帮忙了。你联系小A使用大家之间的『证书』验证其真实性后,由小A使用他和小B约定的『证书』来验证小B的真实性。验证成功后,你也就能信任小B了吧,毕竟你的朋友小A已经信任电话那头的小B了。
这个找小A验证小B的过程,正是TLS中核心的『证书链』机制。
前面说到,在TLS握手过程当中服务端会提供给客户端它的证书。这个证书可不是随意生成的,而是经过指定的权威机构申请颁发的。服务端若是可以提供一个合法的证书,说明这个服务端是合法的,能够被信任。就拿上图来讲
客户端获取到了站点证书,拿到了站点的公钥
要验证站点可信后,才能使用其公钥,所以客户端找到其站点证书颁发者的信息
站点证书的颁发者验证了服务端站点是可信的,但客户端依然不清楚该颁发者是否可信
再往上回溯,找到了认证了中间证书商的源头证书颁发者。因为源头的证书颁发者很是少,咱们浏览器以前就认识了,所以能够认为根证书颁发者是可信的
一路倒推,证书颁发者可信,那么它所颁发的全部站点也是可信的,最终肯定咱们所要访问的服务端是可信的
客户端使用证书中包含的公钥,继续完成TLS的握手过程
整个过程包含两个关键点
根证书颁发机构的权威性须要保证
如何从证书颁发者那里验证证书的合法性
先说说第一个问题,权威的根证书颁发机构很是少,所以浏览器和操做系统都会内置一些可信的根证书颁发机构,也就是说这些机构的权威性是由浏览器或操做系统保证的。但曾经也出现过一些并不权威的证书因为某些目的被内置在中国版的Firefox中这样的事件,让人直呼防不胜防。
好在当前大多数状况下跟证书仍是可以保证权威的,若是是CNNIC这种系统性风险,那也只有靠多关注安全新闻来及时避免了。经过系统或浏览器配置就能查看到本身内置可信的跟证书颁发机构了,这里是我mac的结果。若是发生了像上述CNNIC证书的问题,只要到证书列表这里删除对应的可信证书便可。
再来讲说第二个问题,如何从证书颁发者那里验证某个证书的有效性。每一个证书颁发机构在颁发证书的同时,有义务维护其认证站点的权威性:好比每一个证书都有过时时间,一旦到期就要当即失效;某个站点须要更换证书服务商,换上新的证书时,老的也就当即要失效,不然老的证书可能会被挪用到不法站点上;因为站点自身问题致使私钥泄密,那么TLS的加密就不能保证安全;证书颁发机构被冒名顶替等等。一旦发生以上任何一种状况,证书颁发者都要保证对应证书当即失效。证书颁发者通常提供两种方式来验证证书有效性:CRL和OCSP。
CRL(Certificate Revocation List)即证书撤销名单。证书颁发者会提供一份已失效证书的名单,供浏览器验证证书使用。固然这份名单是巨长无比的,浏览器不可能每次TLS都去下载,因此经常使用作法是浏览器会缓存这份名单,按期作后台更新。这样虽而后台更新存在时间间隔,证书失效不实时,但通常也OK了。
OCSP(Online Certificate StatusProtocol)即在线证书状态协议。除了离线文件,证书颁发者也会提供实时的查询接口,查询某个特定证书目前是否有效。实时查询的问题在于浏览器须要等待这个查询结束才能继续TLS握手,延迟会很大。此外,经过『某个IP请求了某个证书的OCSP请求』这个信息,其实就暴露了某个用户正在访问某个网站了,也算是一种隐私的泄露。
以上是站在证书颁发者的角度说明会提供两种判断方式,实际状况下浏览器究竟会选择哪一种方式判断,每一个浏览器都有本身的实现。下图是经过chrome查看某个网站的证书信息,能够看到其中的证书链和证书验证信息。
中间人攻击
介绍完证书如何保证站点的可信之后,咱们来看看若是证书不合法会怎么样。致使不合法证书还能被使用有两种状况,一种是前文提到的CNNIC证书等相似的系统性风险,另外一种是用户缺少安全意识,手动信任了存在安全隐患的证书。
对于前者,咱们要多关注安全新闻按期排雷,对于后者咱们要提升安全意识,之后看到这些画面时可要慎之又慎了,而不是一味地点赞成。
一旦非法的证书被浏览器视为合法,那么就面临着一种『中间人攻击』的风险:恶意节点会经过持有的非法证书和客户端交互,以后代替客户端,向真实服务器发起请求,并把服务器的请求返回给客户端。这样客户端好像感受本身直接链接了服务器,但实际上有个隐藏的中间人,神不知鬼不觉地窃取了中间的信息。
至此,已经将TLS技术如何应对『明文』和『没法验证服务器的真实性』两个主要安全问题讲完了,因为本人并不从事网工相关职业,所以就没有深究到协议消息帧细节,只是从原理层面进行了理解。若是关于TLS还有什么没有提到,或者理解不正确的,欢迎各位提出来~