引言为什么把这个作为选题。 大概也是2年前,我的同事,在面试某大厂遇到的问题与我一起探讨。这个时候我发现,虽然TLS(https)这个东西大部分时候可能不会被直接用到,但很容易被
这张图展示了http各版本店基本数据传输结构。(该图引至bagder)
从HTTP 1 开始http就在利用TLS确保其安全性(事实上http 0.9就开始在使用SSL)。无论HTTP如何升级,承载其安全特性的组件TLS的地位始终没有被动摇。(从1995年TLS3 首次部署到现在已经超过20年) 值得一提的是作为最知名的传输层协议TCP,HTTP也计划在HTTP3中将其替换,不过其依然沿用最新版本的TLS保障其安全 (举这个例子并不是说TCP不优秀) 上图时间线简单展示了HTTP及SSL/TLS的关键时间线(http1.1 在1999年时有一次定稿,有的资料上会认为1999年是http1.1正式发布的时间点)。不难发现2者的发展一直保持微妙的同步,当然这与web爆发发展有关系,http最知名的版本http1.1 及 现在TLS的基础版本SSL 3 都出现在1996年前后,其影响也一直持续到了现在。 20年前的基础 数据来源 :https://almanac.httparchive.org/en/2021/http 上图是http各版本当前在web系统中的使用占比,不难看出 http 1.1 至今仍然被广泛使用,这个数字在2020年甚至还有50% (虽然统计中HTTP 2+ 超过了7成,但是并不代表这7成服务不支持HTTP 1.1,他们中大部分可以自动协商降级支持1.1) 而1996年被重新设计的SSL3.0 也一直延用至今(1999年发布的TLS1.0与SSL3.0 修改不大,以至于后面有的地方认为他们是同一个版本) PS :HTTP与TSL是完全2个独立的协议,只是HTTPS利用TLS保证其安全性。 TLS其他应用范围 当然不能因为http一直与tls绑定,我们就相信tls在安全方面的能力。 (毕竟大家平时在引入三方资源时都会先确认用“他”的人多不多,大部分会优先信任被广泛使用的资源) 我们常用的SSH利用TLS保证连接的安全性,http的好朋友Websocket也在使用TLS,我们常见的VPN也在使用TLS确保我们的隐私安全。 这些基础事实是大家对TLS的认可,同时也表明TLS在安全传输方面的事实地位。 HTTPS/TLS如何确保数据传输安全 那TLS是如何保证其安全的,为什么其地位20多年都无法被撼动。 密钥交换 数据如何安全传输,当然第一时间就能想到“数据加密”(不过数据的传输安全远不仅如此,除了机密性,还需要有鉴别,数据完整性,和不可抵赖性,这些特性TLS都可以做到,不过不在本文讨论范围) 虽然简单加密是不够的,不过消息加密确实一切的基础。看起来并不难,服务器与客户端只需要有一个只有他们2方才知道的密钥,如何用这个密钥传输数据就可以了。 现在真正的问题来了,这个密钥要怎么才能做的只有客户端与服务器才知道,我们知道在互联网上任何数据包的传输都需要经过许许多多路由器等网络设备,其中任意一台设备都知道你的数据包里是什么内容,我们不可能通过网络直接把这个密钥发送给对方。 有一个方案其实很常见,密钥离线分发。比如常见的U盾,口令卡,银行系统用的分量密钥卡等等都是用的类似离线的手段保证密钥的安全。这种密钥交换的方式的确是安全了不过代价太大,对于广泛应用与互联网的http显然不合适。 公开密钥算法 不过幸好早在上个世纪70年年代就出现了公开的非对称加密算法(公开密钥算法,个人觉得这个名字更能体现这个密钥算法的特点),而且其安全性到现如今依然被证明有效(这也是为什么1995就已经问世的SSL3 可以作为TLS的基础一直延用至今的原因),借助于公开密钥算法使得密钥在客户端于服务器之间的安全传播成为可能。 TLS支持的密钥交换算法有许多(比如RSA密钥交换,Diffie-Hellman密钥交换.......)。我们看下RAS密码交换(因为他被广泛应用,且相对简单),看他是如何保证密钥交换的安全性的。 先介绍非对称加密算法的一个重要特点,加密时需要用到一对密钥(公钥,私钥),公钥加密的数据只能通过私钥解密,私钥加密的数据只能通过公钥解密。- 通常我们说的对称加密基本上都是简单异或或异或的简单变种操作,这正好是计算机擅长的,所以速度非常快。(一般密钥实际上只是一种二进制数据块)
- 非对称加密算法是用的数学算法,无论是加解密,还是密钥对的生成消耗都比对称加密慢很多。(密钥实际上是一个数字,很大的数字)
中间人攻击 看起来是不是很完美,就算密钥交换过程中的数据被其他人知道也没有关系,因为他们没有私钥,没有办法解密,不过还是有漏洞,你连接的网络设备可不是只能看你的数据,他还可以任意截断修改你的原始报文,这就是中间人攻击。
虽然客户端可以使用公钥加密预主密钥,不过客户端的公钥是从哪里来的呢,其实也还是通过网络由服务器下发给客户端的。那既然也是通过网络,在下发公钥的时候工作密钥可还没有生成,这就是说公钥可以被网络中的第三方截获然后替换,客户端以为收到的是服务器的公钥,而实际上收到的是中间人自己生成的公钥(中间人知道与之对应的私钥),这样一来客户端就会在毫不知情的情况下与中间人完成密钥交换(TLS握手),然后中间人会代替客户端与服务端交互,用户及服务端都不知道有中间人的操作,数据就会完全暴露给攻击者。
证书验证 公钥基础设施 现在问题就变成了客户端如何确认自己收到的公钥不是任何攻击者的证书。PKI(公钥基础设施)完美的解决了这个问题。 其实之前就有提到过当前最安全的手段其实就是密钥离线分发,而整个web系统网站这么多不太可能每个网站每个服务器都来给网站离线下发密钥,这个时候就需要一个中间商,一个大家都认可的中间商CA中心(证书签发机构)来帮我们完成服务端公钥的验证,虽然为每个网站离线分发公钥不太可能,不过我们可以提前为客户端离线分发受信任的根证书,利用这个根证书去验证网站服务端发客户端的公钥,这就是前面提到的PKI。 CA中心会提前将自己的根证书加入到操作系统的根证书列表(系统安装的时候就在那里了),许多浏览器也有自己受信根证书列表,这些证书都是内置的,网络嗅探者无法污染这些根证书。 那操作系统凭什么又如何要信任这些根证书呢? 这个就很直白了,刚刚说了是“权威”机构的根证书,你可以不信任,不过基于PKI的证书校验将无法运作,当然这些权威的证书颁发机构也也确实要能对得起他的权威。 现在客户端已经有了离线的信任根证书,那他是如何认证网站发过来的公钥呢(网站发过来实际上是证书,这个证书里包含公钥) 证书验证过程 client会校验证书的合法性,并根据验证结果决定是否连接服务器。 如上图在浏览器中任意找一个https的网页,服务器在建立TLS连接前都会先将自己的公钥证书发给客户端,我们查看其证书信息。 从这里面我们能看到证书包含以下内容: (1) Validity也即有效期,有效期包含生效时间和失效时间,是一个时间区间; (2) 公钥信息Subject Public Key Info,包括公钥的加密算法和公钥内容; (3) 指纹信息,指纹用于验证证书的完整性,也是证书校验的关键,他确保证书没有被修改过。 其原理就是在颁发证书时,颁发者根据指纹算法(单向散列函数)(此处证书使用了SHA-1和SHA-256算法 有多个指纹是为了兼容老的客户端)计算整个证书的hash指纹【证书内容hash值使用CA私钥加密就是指纹】并和证书放在一起,client在打开证书时,自己也根据指纹算法计算一下证书的hash值,同时使用自己信任的根证书的公钥解密hash指纹计算出原始hash,如果hash值不一致,则表明证书内容被篡改过; (4) 证书的签名Certificate Signature Value和Certificate Signature Algorithm,对证书签名所使用的Hash算法和Hash值; (5) 签发该证书的CA机构Issuer; (6) 该证书是签发给哪个组织/公司信息Subject; (7) 证书版本Version、证书序列号Serial Number以及Extensions扩展信息等。 上图即是证书指纹校验的主要过程,不难看出Client校验证书的核心其实是CA公钥解密出原始指纹。那这个CA公钥从哪里来,这就是上文中提到的系统根证书。 这里就有一个关键点证书的颁发,我们现在知道根证书里的公钥是用来解密网站公钥证书的指纹的,为了确保安全系统会有一批自己信任的CA公钥列表(根证书),操作系统会提前内置好这些CA的根证书。这些根证书为什么可以验证网站的证书呢 ,因为网址的证书也是这些CA机构签发的。CA对应的一般是权威机构或组织,然后由这些权威机构颁发证书时使用他们自己的私钥去签名(为证书生成指纹)然后再颁发给网站,这样就确保了只有权威机构颁发给各个网站的证书才会被客户端校验通过。 网站证书的颁发 顺便我们看一下CA颁发给网站的证书具体是怎样的,一般有2部分组成 公钥证书:这个就是网站后面会公开发送给客户端的证书 (CA的私钥只会用来给这个证书签名,不关心证书里的密钥) 私钥文件:里面只有一个私钥(与公钥证书里公钥是一对,用来在TLS握手只解密预主密钥) 如上图sipv4.com.key就是私钥文件,sipv4.com.cer就是公钥证书 (如果看的细,会发现cer里不止含有一个CERTIFICATE,其实他是一个证书链) 上图简单描述了证书如何在TLS里发挥作用。有一点需要注意,CA再为网站颁发证书时会验证申请者是网站的拥有者,这样可以确保只有网站的拥有者才可能拥有合法的证书。这一通操作下来即使中间人用自己生成的公钥证书来欺骗客户端时,客户端也能马上反应过来从而中止链接。 现在我们再来看下攻击者的处境,攻击者如果伪装成网站A,握手时攻击者需要给用户发送公钥证书,攻击者有2个选择。- 发送真正的证书(因为公钥证书是公开的,任何人都可以方便的获取),这样虽然可以通过客户端的证书校验,不过攻击者因为没有私钥,无法解密客户端发过来的预主密钥,握手会最终失败。
- 发送攻击者自己生成的公钥证书,这时虽然攻击者有对应的私钥,不过因为自己生成的证书没有CA的签名,无法通过客户端的证书验证,握手也会被终止。