ihciah / shadow-tls

A proxy to expose real tls handshake to the firewall
https://www.ihcblog.com/a-better-tls-obfs-proxy/
MIT License
2.32k stars 265 forks source link

[Discussion]ShadowTLS v3 #69

Open 3andne opened 1 year ago

3andne commented 1 year ago

Hi @ihciah Just took a closer look at the v3 protocol. The idea of auth padding in application records looks awesome. This would allow the server to detect anomalies without the help of Shadowsocks and make it payload agnostic. That said, there are some issues that I feel should be pointed out.

  1. The design is vulnerable to replay attacks for TLS v1.2 full handshake. There isn't anything like a pub key in a TLS v1.2 ClientHello when performing a full handshake. The attacker can start with the same clienthello and complete the handshake. The complexity of Restls is partly due to the goal of TLS1.2 compatibility.
  2. The auth padding on a TLS application data record doesn't work for TLS v1.2 since the server doesn't have a chance to send any 0x17 during the handshake. By the time the client sends the first 0x17 in the entire handshake, it has no idea if the server is genuine.
  3. Adding auth padding to handshake records might introduce new characteristics - even though handshake in TLS v1.3 is mostly encrypted, the length of that doesn't vary from request to request. The attacker can actively probe a v3 server, and record the length of the first encrypted packet (ServerEncryptedExtension in this case). If that's mostly, if not always, 4 bytes shorter than the ServerEncryptedExtension sent to the suspect v3 client, then it has a chance to identify the server.

----------------

我仔细研究了一下v3的协议,觉得有很多可取之处。特别是在0x17中使用auth padding的做法,这可以在没有Shadowsocks(或者其他协议)的帮助下监测异常,使得协议可以更通用。 但我还是发现了一些问题:

  1. v3无法防止针对tls 1.2 完整握手的重放,因为tls 1.2的clienthello里没有任何类似于公钥的东西(除了session ticket),攻击者完全可以使用完全一样的clienthello开始握手。Restls的设计之所以会显得如此复杂,一部分原因就是为了实现对TLS 1.2的支持
  2. 在0x17中添加auth padding的做法也不适用于tls 1.2,因为tls 1.2里第一个0x17是客户端发的,然而它还不知server是否被劫持。
  3. 对握手包添加auth padding的做法可能产生新特征。即便TLS 1.3的大部分握手包都是加密的,他们的长度却是相对固定的,尤其是服务器第一个加密的握手包ServerEncryptedExtension。如果墙主动探测获得的ServerEncryptedExtension的长度永远(或大多数时候)都比一个疑似客户端收到的ServerEncryptedExtension短4个byte,或许墙就能发现这是个client。针对这个问题的规避也导致了Restls实现的复杂性。
ihciah commented 1 year ago

谢谢大佬 Review!

  1. 防止 ClientHello 重放:后续在发 0.3 版本之前会增加协议版本的限制,关于细节我还没有深入研究。
  2. 客户端发送 AppData 一定是握手正确结束,此时服务端已经证明身份。注:此处客户端并非是在第一个 AppData 处添加 HMAC,而是握手结束后的 AppData。
  3. 感谢提醒,这里设计padding的目的是在不影响 TLS 库的情况下鉴别流量来源,更利于实现。长度问题后续我会验证下。
ihciah commented 1 year ago

关于 V3 协议这里写了一个面向普通用户的 Wiki:https://github.com/ihciah/shadow-tls/wiki/V3-Protocol

3andne commented 1 year ago

客户端发送 AppData 一定是握手正确结束,此时服务端已经证明身份。注:此处客户端并非是在第一个 AppData 处添加 HMAC,而是握手结束后的 AppData。

TLS 1.2里,server在整个握手过程中都不会发送0x17,如果我理解正确,padding是只加在0x17中,客户端在发送第一个0x17(真正的application data)时,并不知道服务器的身份。

此问题在tls 1.3中并不存在。

ihciah commented 1 year ago

存在 SessionTicket 的情况下似乎会有 Server 侧主动发送的 AppData。我试了下我这边的 V3 版本在 cloud.tencent.com 是能使用的(如果服务端没能证明身份,客户端会视作连接被劫持,无法正常代理流量)。 (被 cloud.tencent.com 误导了,这个域名在海外用的CDN是支持 tls1.3 的,国内的不支持)确认问题存在。

我的想法是 ShadowTLS 后续的工作是明确这些边界,在代码中加以限制,并告知用户;但不从协议和代码上特地对 TLS1.2 做强制处理(至少要是一个可选实现),因为这样会引入非常大的复杂度。

目前在考虑的一个 idea 是利用 session id 里随机 28 byte 的末 4 byte 再携带一个签名,server 侧如果打开了 strict 开关则会去校验这个签名。这个后续会被细化为一个 V3 的扩展校验协议。 引入扩展协议会增大复杂性,同时确实无法解决没发送0x17的问题。

我这边计划是 TLS1.3 only 了。

3andne commented 1 year ago

存在 SessionTicket 的情况下似乎会有 Server 侧主动发送的 AppData

TLS 1.3设计时的目标就是把自己伪装成一个TLS w/ session resumption,所以在某些时候可能会和TLS1.3流程类似。