强烈建议先阅读上篇文章补充上下文。
上篇我们讲到了常规审计功能,员工访问招聘网站多少次,访问多久时间,这些只需要知道访问域名(DNS 明文、SNI 明文泄露)+ TCP 连接时间就能做到,不需要对 HTTP 内容解析。那么在不借助代理,纯靠端到端加密能否能解决这个问题?
本文的顺序是 DNS => TLS , 但 TLS 才是对应用层更有价值的部分,如果对 DNS 加密不太感兴趣可以跳过。
DNS 安全性
DNS 协议在设计之初并没有考虑到安全性问题,整个DNS协议在互联网上都是没有任何校验的明文传输的。 这意味着任何中间人都能直接篡改或抢答它的返回值。
DNS over HTTPS : 隐藏 DNS 查询动作,解决抢答和加密传输
DoH 可以简单理解为使用一个普通的 HTTPS 请求来解析 DNS ,执行下面命令体验
curl --http2 -H "accept: application/dns-json" "https://1.1.1.1/dns-query?name=cloudflare.com"
由上篇可知 HTTPS 的端到端加密可以信赖, 则 DoH 只是一次普通的 HTTPS 请求,隐藏了常规的 DNS 操作 + 隐藏了查询的目标域名 + 避免了返回值被替换。 可知 DoH 是一种解决手段,可以堵上 DNS 查询暴露请求目标域名的漏洞。需要在客户端,如 Win10 系统或 Chrome 配置开启,了解更多 。
提示一下如不了解,不建议直接开启这个功能,因为 DNS 和 CDN 网络链路优化强关联,了解更多,DoH 的最佳实践比较复杂,往往还是需要借助代理,但在代理的场景下其实有兼容性更好的 DNS 处理方式,比如 fake-ip。
DNSSEC:解决改写问题,层级 DNS 携带公钥、签名记录
与 DoH 同级,相比 DNS over HTTPS, DNSSEC 并没有加密 DNS 查询过程,了解它的意义在于:
- 主要是服务端配置,客户端无需额外的配置,和 DoH 不同
- 象征了 DNS 功能的升级。DNS 记录除了常规的 A、AAAA、CNAME 等记录,也有专属记录如 DNSKEY(公钥),DS(delegation signer ,DNSKEY 的 hash 摘要),RRSIG(DNS 记录的数字签名)来实现类似上篇 CA 证书链认证 的过程, 了解更多 DNS 记录类型。
以一个配置在 CloudFlare 的域名配置 DNSSEC 为例验证上述记录:
图片右键新窗口打开可清晰查看
个人域名 test.cc 的 DNS 解析过程 root . => .cc => test.cc 都存在 DNSKEY (公钥)记录,并有 RRSIG (签名)顺序验签,和上文 CA 证书链类似,只是这里信任链起点根证书是 DNS 根服务器(非本地),全球单点,所以它的签发比较复杂,了解 DNSSEC 根签名仪式。
第二层:审计访问目标站点,进行阻断监听或特征行为捕捉
上文的 DoH 功能需要客户端手动开启,比如在Chrome 里是 “使用安全 DNS”。
DNS 是链路安全上比较大的隐患,反方向来说它也是网络审计系统的重要依赖,所以公司电脑如果配置了浏览器管控,则会禁用 安全 DNS 功能,可以打开公司电脑看看。
虽然 SNI 也会明文暴露访问域名,但如上篇提到 TLS 握手最后会发 Session ticket ,往往一天左右才过期:
没过期时客户端可以直接用 Ticket 恢复 TLS 会话,之前交换的 session key 中间人不知道,所以sessionId 明文没关系。但在这种情况下就不能精细化审计,比如图片里连接网站的次数:
TLS 恢复会话时中间人不知道访问的网站,所以根据 DNS 查询能更好判断访问次数。当然如果知道 IP ,通过 TCP 连接次数也能知道,只是 IP 会变,不如 DNS 查询准确。由此审计功能的能力边界也极其依赖对设备的直接限制,所以只常见于企业使用。
应对方式:没什么好办法,被企业管理的设备往往无法修改关键设置,很多时候息屏时间都无法修改,并且即使端到端安全了,系统内仍然可能靠端内的应用来监听工作时长,截屏的软件来实施监控。能做的只有尽量不用这种设备以及连接办公网络。
但一定想在这样的设备做自己的事也是有办法的,见 VS Code 打造个人超级工作站,我们已知常规端到端加密至少内容加密是可靠的,那打开 VScode 远程连接一个机器(端),在那个端做自己想做的事就行了。这实际上是一种代理,真实的网络请求都在另一个端上完成。
TLS 安全性
TLS 随版本迭代才不断完善,因而 TLS 降级是常见的中间人攻击手段。
什么是 SNI
上篇文章提到过,TLS 握手第一步 Client hello 为明文,包括支持 TLS 的版本(中间人篡改后就是降级攻击),SNI Extension 直接暴露了 server_name , 因为一个 IP 可以部署多个服务域名,需要告知以返回正确的 CA 证书。
这里还有一种例外情况,如果一个 IP 只部署了一个域名就不用传 SNI 字段,所以 DNS 污染对中间人仍然必要。
ESNI / ECH :堵死 server_name 暴露的最后方式
ESNI = encrypted SNI , ECH = encrypted Client Hello ,都是加密 Client Hello ,减少明文信息。
TLS 1.3 对 CA 传输加密
值得一提的是 CA 明文暴露给中间人也是不安全的,因为 CA 往往也对应一个明确的域名,所以 TLS 1.3 是加密传输 CA 的, ESNI 也需要 TLS 1.3 ,不然加密了 SNI 但没加密 CA 就是掩耳盗铃了。
上篇介绍的 TLS 握手过程实际是 RSA 握手版本的,步骤在客户端 CA 验签完成后,确认服务端公钥未被替换后,非对称加密传输 Premaster key,两端此时同有 Client random + Server random + Premaster key => 生成 session key 。
Diffe-Hellman 算法
普通 Diffe-Hellman 算法相对易于理解,如图:
n次方后取模这个计算的特点是正向计算容易,逆向向计算困难,即单向函数(One-way Function),如哈希算法。图中利用公开的 p=23 ,g=5 结合双方自己的 random 值就能简单算出 shared secret key 。
- 客户端有 私钥a=4 ,服务端有 私钥b=3 ,各自通过公开的 p=23 , g=5 计算出 公钥A=4, B=10
- 因为单项函数的特性,在双方交换单项函数计算结果 A=4 , B=10 时,中间人很难逆向出原本的 私钥a,b 值
- 双端用交换后的 A,B 值结合自身的 a,b , 可以算出相同的 shared Key s=18
- 这里还有个点在于之后不是直接用 shared key 直接对称加密,而是再派生出 client key , server key , 客户端只用client key 加密,反之亦然。因为如果双端用相同key 加密,虽然中间人不知道 key, 但可以不断重放加密后的数据来扰乱通信。
相比 RSA 握手,生产的 shared key 都是会话级别的,拥有了前向安全性。
- DH 算法也能被中间人攻击吗
值得一提的是,单纯的 Diffe-Hellman 也防止不了中间人攻击。如同使用 RSA 时中间人可以完全替换成自己的公钥和私钥,如果 Diffe-Hellman 交换时不加密,中间人也可以完全在中间伪装所有交互,在中间对双端各自生成 key share。 验证端身份的唯一方式还是客户端 CA 本地验签和数字签名,见下文
Server hello 如何加密 : Encrypted Handshake Messages
-
发送 server hello 时,虽然client 还未获得 DH 参数交换,但 Server 已经可以算出相同的 shared Key s=18 了,所以 server hello 中 key share( DH 算法参数明文)后的 CA 传输, CA verify , finish 消息都已经使用 keyshare =18 加密。
-
在传输 CA 后还会传输 CA verify 消息,包含对前面全部信息(包含 DH 参数)的数字签名。
-
假如中间人想代理全部信息
- 此时中间人拥有明文的 CA , DH 参数,数字签名,中间人需将三者都替换为自己的然后反给 Client
- 中间人虽然通过明文 CA 知道了 Server 域名,但 CA 验签信任链只在 Client 本地,所以替换 CA 会导致握手失败
-
假设 MITM 试图只监听,不篡改
- MITM 看到
ClientHello,包括ECDHE key_share(公钥部分 A = 4) 。 - MITM 看到
ServerHello,包括服务器的ECDHE key_share(公钥部分 B = 10)。 - MITM 没有客户端和服务器的私钥 a=4, b=3,因此无法计算
shared_secret= 18。 即只有真正的两端进行握手时 ,才能算出 18 ,中间人分别和两端握手,是算不出这个值的,只能各自生成新值,比如 17 、 19。 - ECDHE 的特性决定了想当中间人必须计算出不同的 key share, 则会破坏数字签名导致握手失败
- MITM 看到
由此既加密了 CA 信息,又借由 CA 验签过程保证了 CA 本身不被替换,或被中间人代理解密看到。
TLS 1.3 禁用了 TLS 版本降级,并且固定了密钥交换算法只能是 DH 算法 ECDHE(Elliptic Curve Diffie-Hellman Ephemeral) 。
在 TLS 1.3,把 ECDHE 密钥交换提早到了 Client hello + Server hello 里, 从 Server hello 时就能对称加密业务数据。流程上之后再传输 CA,从而将 CA 也进行了加密。
下图是 TLS 1.3 的 Client helllo + Server hello,ECDHE 的 key share 塞进了 Client hello 和 Server hello 的 extension 字段里
如上图,可见 DH 算法参数 key share 为明文交换。
不再能看到 Certificate 步骤,因为已经被对称加密,变成两个 Application Data。第一个是 Certificate 消息传证书内容,第二个是 Certificate Verify 消息,Server 对握手信息用私钥签名,Client 用证书里公钥解密来防止中间人攻击。
-
证书(Certificate):这一部分负责携带服务器的公钥和一些其他信息。这份证书由各自的证书颁发机构(CA)颁发,并且在TLS握手过程中发送给客户端。这份证书不会包含任何和DH参数相关的信息。
-
证书验证(Certificate Verify):这一部分是用服务器的私钥(对应于证书中的公钥)对前面所有握手消息的摘要进行签名的结果。在收到这一部分后,客户端可以用之前收到的证书里的公钥进行验证,从而确保握手过程中的消息(包括DH参数)没有被篡改。
-
说到底,对中间人的防范全在于对于特定权威域名的证书无法伪造这一点上。如果客户端被装 Root CA 破除这一点,就能做到审计效果。
单纯的 RSA 非对称加密传输和 ECDHE 交换实际上都防止不了中间人攻击,真正验证端身份的唯一方式还是 CA 验签,并使用证书的公钥验证数字签名,因而对于中间人,无论哪种秘钥交换方式,替换证书及公钥都是必要的。
这里也体现出握手最短由 2RTT 优化到 1RTT,即 client hello + server hello 后 DH 算法秘钥已经生成,Server hello 就能回传业务通信,速度提升明显。而恢复 TLS 会话时 Client hello 也能传业务数据,即 0RTT 恢复会话功能,但有重放风险。
ECDHE 前向安全性:相比较而言基于公钥的非对称加密的算法没有前向安全性,比如服务端私钥泄露了,之前的网络包数据记录就都能解密。而 ECDHE 的 key 是会话级别的,所以不用担心之前会话记录被破解。
下图是 TLS 1.2 时看到的的 ECDHE 秘钥交换方式
Client hello 如何加密
在 TLS 握手第一步,服务端还未向客户端发送任何信息,此时加密公钥哪来?TLS 握手前唯一发生的事是 DNS 查询,所以是从 DNS 里查公钥。可以查看上文 DNSSEC 部分,通过 DNS 记录传递公钥,也是 ESNI/ECH 的必要依赖,更多细节可查看下面文档。
真正的端到端加密
上面的 DoH 和 ECH 的共同点在于对客户端服务端都有要求,兼容性较差,实际上代理服务器有更好更简单的处理方式:使用中转来 DNS 查询,且这个中转服务器隐藏了请求对象真实域名,还顺带解决了 IP 黑名单。但代理说到底是一个新的端,如果一定要引入新的端来解决,无法分析端到端加密的可靠性。
完全无代理的方式更能体现端到端加密的发展阶段,所以这里结合上文已经了解的点,总结下业界前沿的端到端加密如何解决中间人攻击切入点:
- DNS 明文:DoH ,DNSSEC 等方式加密
- IP 黑名单: IPv6 可有条件解决,关于 IPv6 可以自行了解
- SNI 明文:ESNI、ECH, 随着 TLS 版本提升,明文信息越来越少
以上方式全上齐,中间人无法获取任何有用信息(访问域名),无法做任何审计了。做到这一层,起码任何网关类型的监控,比如离职倾向分析这种业务当然无法实施了,中间人面对的只有茫茫多的加密数据包。可以说,做到了真正的端到端加密。
第三层:深度包检测,根据流量特征阻断
到了这一层,则终于引入中间人攻击发展阶段的最前沿:中间人面对两眼一抹黑的数据包,能做什么有意义的操作?没错,只有无条件阻断。准确来说是根据加密数据包的统计学特征,结合机器学习等算力投入来只执行阻断这一项工作。
在这种情况下,中间人不知道你访问了哪个网站,只知道从茫茫多的数据包中找到那些个有 ESNI/ECH 特征的数据包,并直接丢弃,相当于封禁了 TLS 1.3 的这个功能。
这也刚好反证了端到端加密的有效性:如果有任何可利用的漏洞暴露有效信息,中间人不会采取这种需要堆算力的审计方式,相当于单点对抗全网流量。
解决方式:手动混淆流量特征,比如每个包加入随机长度的字符串。实践上,DoH,ECH 都还用不上的情况下,实际上端到端加密和中间人的博弈只发生在 客户端 连接到 代理服务器 的这个过程里。
总结
最后结合上下文,对中间人攻击按能力排序进行总结,并给出简单应对方式。
第一层:中间人手动安装 Root CA,并基于直接替换目标域名的 CA,完全解析 HTTP 内容,为所欲为,但一般只见于开发场景,且容易发现。手机装的 Root CA 往往真的只用于认证 WIFI,没有监听功能。
解决:注意当前网站 CA 是否被替换即可,并不用担心应用程序(如 WX)被完全监听。
第二层:中间人基于 TLS 低版本的各种漏洞,获取连接的各种元信息来审计,能力局限于访问目标,连接时间。一些企业设备支持审计少数应用特征行为(上传、登录),但能力强依赖对用户设备的限制(禁用安全DNS,禁用部分应用)
解决:一般不能修改被管理的设备,所以尽量避免使用公司网络和设备。实在要用设备,可以通过 VS Code remote dev 这种加密连接到新的端来保证安全。
第三层:中间人同时采取 DNS 污染和 SNI 漏洞等上述全部方式,在都不生效,任何连接的真实内容都被成功加密时,中间人完全根据加密流量报文的统计学特征来阻断。
解决:对中间人加密全部的有用信息(包括隐藏目标 IP,防止 DNS 污染,隐藏 server_name) 的同时,抹平混淆数据包流量特征,它象征着中间人攻防技术螺旋上升的最前沿,如果有兴趣可以参考这篇文章。
写文章本身也是一个学习的过程,也请读者能指出文章中的疏忽错漏之处。如果本文对你有所帮助,欢迎点赞收藏。