HTTP 要点回顾
由于 HTTP 是基于 Readable String 的协议体系,取决于其设计原理,传输内容没有校验机制,而且很容易被篡改
加密传输基础问题
对称加密
最简单的加密方法就是对称加密,使用加密算法和 Key 将明文转化成持有 Key 的用户可以解读的密文
1 | //加密算法逻辑: 明文+Key 通过加密算法得到 密文 |
作为对称加密的经典思路,通过简单思考很容易引发(问题1-1)
- 问题1-1: 如何传递Key,是通过物理传递还是网络?传递人或者传递通道是否可信
非对称加密
而非对称加密算法可以很容易的解决对称加密引发的问题
1 | //加密算法逻辑: 明文+公钥Key 通过加密算法得到 密文 |
根据非对称加密的思路,PublicKey 是可以任意传递和分发的,而 SecretString 也仅仅能被 PrivateKey 解密
那么在对称加密的问题1-1对应的解法就有了
- 解法1-1: 因为 PublicKey 可以任意传递,而 PrivateKey 仅能被自己持有(任何时候都不能传递),不需要担心传递过程中的传递人和通道可信程度
在通信的过程中,按照 C/S 架构设计通常存在 Server 和 Client 两个终端
完成了 Client–>Server 加密逻辑,如何保证返回报文 Server–>Client 也具备加密能力,就是(问题1-2)
- 问题1-2: 如何完成点对点的加密能力
点对点加密
在对称加密的设计下 Server 和 Client 公用一个Key,加密解密都使用这个Key即可, 时序图
1 | //ReqStr: Request String ResStr: Response String |
而在非对称加密的设计下
因为 PublicKey 加密后仅有 PrivateKey 可以解密,而 PrivateKey 又不能传递(不然就需要面对和对称加密一样的问题)
那么在非对称加密的设计下,点对点就需要两个密钥对(共计4个密钥)
密钥 | 作用 |
---|---|
Server-PublicKey | 用于加密Client–>Server的通信 |
Server-PrivateKey | 用于解密,不应该以任何渠道离开Server |
Client-PublicKey | 用于加密Server–>Client的通信 |
Client-PrivateKey | 用于解密,不应该以任何渠道离开Client |
根据 对称加密 和 非对称加密 的设计,我们可以得到时序图
1 | //C-PubKey: Client Public Key C-PriKey: Client Private Key |
通过时序图,我们可以得到
- 解法1-2: 为了实现点对点加密,对称加密通过 Client/Server 使用同一个Key,而非对称通信通过 Client/Server 各自生成一对Key(共计4个)来完成
在通常的互联网服务中,大多数都是多个客户端和一个服务器的多对一服务,在多对一的设计中必然存在以下问题:
- 问题1-3: 如何保证每一个通信之间加密的独立
- 问题1-4: 如何保证密钥的更新逻辑
密钥的分配和管理
在对称加密中,因为使用同一个Key,那么
- 问题1-3必然需要无数个密钥来对应不同的通信
- 问题1-4每次都需要通知到对应的客户端来完成更新,而且每一次执行,都会再次面临问题1-1
而非对称加密中因为 Server–>Client的 Response 是由 ClientPublicKey 加密的
而ClientPublicKey 必须和 ClientPrivateKey 配对使用,从设计上保证了问题1-3
而非对称加密对于问题1-4的解决方案,则是 HTTPS 的核心理论密钥交换机制 从结论上说,密钥交换从根本上解决了非对称加密的问题1-3和问题1-4
HTTPS 密钥交换
密钥交换的基本逻辑
HTTPS 的密钥交换设计保证加密通信的以下特性
- 加密是点对点的,每个Client都有自己的密钥对,C-PrivateKey永远不离开终端本身,保证了密钥不会被其它通信复用
- Server\Client 密钥更新可以做到独立,每次通信都可以使用新的密钥,使密钥的时效性更好
密钥交换原理中最重要的是 一旦密钥交换完成,通信就可以认为没有任何被破解攻击的可能,时序图如下
1 |
|
由于 Client 每次发起通信请求,都可以先去服务器获取到最新的 ServerPublicKey
而 Client 在获取到 ServerPublicKey 之后可以即时生成 ClientKeyPair 保证了 Client 的更新
在传递 ClientPublicKey 的过程中,已经被加密过了,仅有 Server 可以用 S-PriKey 读取
Server 读取 ClientPublicKey 成功后,之后的通信就可以完成点对点的加密能力
根据密钥交换设计,每次通信建立 Server/Client 均使用的是最新的私钥,在通信关闭后可以直接丢弃不需要储存,从而得到问题1-3、1-4的解法
解法1-3、1-4: 非对称加密可以通过密钥交换机制来保证不同加密会话的独立,并且规避了密钥更新同步的问题
根据密钥交换的假设,一旦交换成功,会话就是99.9%安全的,那么加密问题被转化为一个简单的问题
问题2-1: 密钥交换过程中哪里会被攻击
密钥攻击的弱点
密钥交换过程最大的攻击方式,就是中间人攻击
而攻击的方式就是在第一步,由一个中间人伪装服务器和客户端,分别和两方完成密钥互换,从而完成会话攻击
1 | // M-PubKey: Middle Public Key , use for cheat client as S-PubKey, cheat server as C-PubKey |
根据时序图,我们可以发现,第一步就存在一个中间服务器,使用一套密钥对 M-PubKey、M-PriKey 来进行伪装,那么他就可以截获所有会话内容
中间人密钥 | 对客户端 | 对服务端 |
---|---|---|
MiddlePublicKey | 伪装成ServerPublicKey下发给客户端 | 伪装成ClientPubKey发送给服务端 |
MiddlePrivateKey | 用来解密Client的消息,相当于ServerPrivateKey | 用来解密Server的消息,相当于ClientPrivateKey |
通过以上分析可以得出,密钥交换的过程中进行攻击的基础,问题2-1的答案就是
- 解法2-1: 密钥交换的第一步,Client 发出 Want S-PubKey 索要申请,被中间人截获,是整个攻击的基础
在分析了攻击点之后,问题2-1就会引出问题2-2
- 问题2-2: 如何确定 Client Want S-PubKey 的请求,返回的PublicKey是真的,而不是被替换过的 Middle Public Key
首先给出答案的思路,问题2-2 的解决方案需要两个基础
- 数字签名技术
- CA证书体系
数字签名技术
首先需要明确的概念,在非对称加密通信中的常规加密思路, PublicKey 加密, PrivateKey 解密
而数字签名技术,则是 PrivateKey 加密, 而 PublicKey 解密,整个逆向的过程称之为签名
功能 | 加密密钥 | 解密密钥 |
---|---|---|
非对称加密 | PublicKey | PrivateKey |
数字签名 | PrivateKey | PublicKey |
两种使用方式,都基于非对称加密,其中签名基于的前提是 PrivateKey 永远只能被终端持有,不应该以任何方式传播出去
1 |
|
CA证书体系
回到问题2-2本身,如何让 Client 收到 S-PubKey 之后,能够确定这个 PublicKey 是没有被篡改过的
最简单的一个思路就是,存在一个第三方,可以明确告诉 Client 这个 S-PubKey 是不是可信的,这个第三方就是CA(Certificate Authority)
CA运作的技术基础就是数字签名技术,通过引入第三个密钥对 CA-PublicKey、 CA-PrivateKey 然后分为两个模块来完成
模块 | 作用 |
---|---|
证书 | 颁发给各个互联网服务商,也就是每一个HTTPS的服务器,通过 CA签名证明 S-PubKey 是真实有效的 |
PKI | 委托生产商把 CA-PublicKey 安装到每一个终端(每一个笔记本、每一个iPhone),用于验证证书签名 |
其中PKI是(Public Key Infrastructure)的缩写,中文叫 公钥基础建设 是一个非盈利组织在管理
PKI 的基本要求是把 CA 的证书预装到每一个合法生产的设备上, 通过联合设备生产商规避了问题1-1
每个设备都有一个默认的列表,比如iPhone好像是有8个
这些 CA-PubKey 也是以证书的形式存在于手机中,是 CA 自己颁发给自己的证书,对自己的 PublicKey进行了签名
1 |
|
通过引入 CA的第三个密钥对 和 基于数字签名技术,我们得到了问题2-2的解决方案
- 解法2-2: 通过对每个服务器的S-PubKey进行签名,证明其有效性(证书),并且委托生产商传递CA-PubKey来验证证书签名,确保了 S-PubKey 可信
通过以上分析,如果再深层次分析引出了问题2-3
问题2-3: 整个PKI+证书的体系是不是完美的,存在不存在攻击的可能
解法2-3: 整个体系其实是非常脆弱的,攻击点大概率存在于Client端,其次是Server端
首先给出答案, 整个体系其实是非常脆弱的,攻击点首先存在于Client端,稍后会列举几个常见的例子
继续思考,除了Client之外,Server存在不存攻击的点,比如DNS劫持等等,不管任何手段都想达到让 Client 误认为假的Server为真,那么就可以引出问题2-4
- 问题2-4: 证书可不可以伪造,申请机制如何运作
对证书进行伪造
如果相对证书进行伪造,需要考虑两种可能
攻击方案 | 方案条件 |
---|---|
方案-A | Client 终端受到控制,终端上 PKI 已经被攻击者攻破 |
方案-B | Client 终端不受控制,并且 PKI 完善 |
攻击设备的 PKI 体系
首先来考虑 方案-A 的前提条件,就是攻击设备的 PKI 体系
攻击 PKI 体系是最简单的,整个 PKI+Cert 的前提是设备内由生产商预装 CA-PubKey 是没按照预期安装好的,没有被篡改过的,如果想要篡改
手段 | 例子 |
---|---|
恶意软件在越狱的手机直接修改 | Android Su权限、iPhone越狱 |
诱导 Client 信任一个不是CA的证书(非法的PublicKey) | Charles、铁道部12306 |
在完成了 PKI 攻击之后,我们可以添加任意的 PublicKey, 假设存在攻击密钥对 Fake-PublicKey、Fake-PrivateKey
我使用 Fake-PriKey 签名的任何证书都可会被设备认定成合法的CA证书
在方案-B中,我们无法控制设备的 PKI 列表, 也就是说必须有一张被真实CA机构签发对证书
那么就需要先解决 问题2-5:
- 问题2-5: 如何申请一张CA机构签发的证书
如何申请一张CA的签发
最简单的解决方案就是买
其次是通过CA机构的工具联网申请,例如 LetsEncrypt 是一个2015年成立免费SSL证书组织,其工具叫 certbot 可以在各大软件源install
申请和申请QQ号没什么区别,主要是提交你想申请的域名,以及最重要的是验证方式(Challenge)
1 | # --manual 代表手工更新,推荐使用自动更新,这里仅作讲解 |
可见如果想通过CA的证书申请,其中 challenge 的方式决定了我们如何通过验证
Challenge | 验证方式 |
---|---|
dns | 在域名的 DNS 解析服务商里添加一条指定的记录,一般是一个TEXT记录的加密文本 |
tls-sni | 在域名指向的服务器443端口修改服务器配置文件 |
http | 在域名指向的服务器80端口放置指定的文件 |
此时我们可以得出问题2-5的答案
- 解法2-4、2-5: 如果想要获取一张合法的CA证书,需要提交申请的域名,并且通过Challenge
那么回到问题2-4,在申请机制的过程中,我们如何进行伪造来通过 Challenge 从而获取合法的证书
攻击CA证书 Challenge 服务
首先我们可以发现 CA 的验证逻辑是,如果申请者可以控制域名的DNS服务(dns)或者主机文件(http、tls-sni),就认为该申请者是域名的合法持有者
- 控制DNS服务: 每个域名都有自己的DNS解析服务商,那么需要盗取该域名持有者的DNS服务器商账号密码,才能控制DNS解析
- 控制主机文件: 可以ssh登录到主机,或者拿下web shell的权限,或者物理接触到服务器主机直接插网线,通过Reset等方式重制密码
至于可以通过何种方式获取到所需业务的账户名和密码,以及如何物理接触到主机,常见的有弱密码破解或者钓鱼邮件等方式,就不展开讨论了
PKI+CERT 体系复盘
通过总结,我们可以发现整个 PKI + CERT 安全体系,是需要多方配合的,而且是十分脆弱的
攻击终端的成本比起伪造证书要低很多,不管是 Client 自己操作不当导致终端的 PKI体系 被破坏,还是由于 Server 管理不善造成证书伪造,都有可能进行中间人攻击
实际案例分析
证书 HardCode 在App里防止 PKI 被攻击
为了防止设备被破解,我们可以通过 SecTrustSetAnchorCertificates 设置PKI列表,通过官方文档可以看到,如果传入NULL则是使用默认的预装证书
1 | //A reference to an array of SecCertificateRef objects representing the set of anchor certificates ...... |
固件升级签名验证
固件升级验签应该在哪里,固件是由网络下载到终端,安装前为了防止固件被修改过,肯定需要验证这个固件的签名,按照逻辑终端PKI本身是可能被攻击的,所以选择网络验证签名
Glance
1 |
|