shadowsocks / shadowsocks-org

www.shadowsocks.org
MIT License
842 stars 524 forks source link

设计一个随机密钥滚动下发机制,实现前向安全、防长期重放、防潜在攻击等 #177

Open RPRX opened 3 years ago

RPRX commented 3 years ago

Shadowsocks 现有的设计存在一个非常明显的问题:无前向安全。而对于 TLS,非 FS 套件早已被视为不够安全了。 “前向安全”指的是攻击者拿到现有的密钥时无法解密过往的通讯内容,实现前向安全需要依赖动态密钥及可信源。

由于现在的国产安卓系统都有云备份功能,手机上的 SS 密码没有安全保证,它可以被用来解密一切通讯内容。

此外,由于 SS 的加密不依赖于时间戳,防重放只能靠缓存,但缓存并不是无限的,这就导致实质上的 无法防重放。 虽然 VMess 也没有前向安全,但它的头部加密依赖于时间戳,至少做到了防重放(不过,时间戳并不是唯一解)

顺便提一下:Shadowsocks 没有 UoT 结构,各个实现都是 TCP、UDP 同端口,这并不常见,是非常明显的特征。


为了解决现在的一些问题,SS 需要设计一个随机密钥滚动下发机制,以下是我的构想:

  1. 首先,这只是内层明文结构的改变,复用现有的加密方式,比如 AEAD 系列
  2. 初次配置时,初始密钥 数等于客户端数量,需通过可信通道传递(显然这不是问题,SSH 或 HTTPS 均可)
  3. 对某个客户端而言,初次连接服务端时用的是初始密钥,并发连接也是如此
  4. 对于服务端,当某个密钥被使用了一定次数(按连接计),后续的连接返回数据时需要带同一个新的 随机密钥 一起返回
  5. 客户端一旦收到了新的密钥,就要存起来,新的连接必须使用它,同时逐步忘记旧的密钥(直到旧的连接全关闭)
  6. 服务端对于同一个客户端,只存最近的几个密钥,而且每个密钥都有 使用次数限制
  7. 注意客户端只持久化收到的最新的密钥。若新增了一个客户端,需要服务端下发新的初始密钥(可以 API 接口)

通过上述机制可以实现:

  1. 前向安全。即使攻击者拿到了现有的密钥,也无法解密绝大多数过往的通信内容。
  2. 后向安全。即使攻击者拿到了现有的密钥,他还必须能拿到后续的所有连接,不然跟不上密钥的更新节奏。
  3. 防长期重放。很简单,因为服务端只存最近的几个密钥。
  4. 防潜在攻击。很简单,因为限制了每个密钥的使用次数。至于是什么攻击,过几天才可以说。
  5. 限制设备数。

可以看到,相对于现有的机制,上述机制并没有明显增加配置复杂度,却大大增强了各方面的安全性。欢迎补充建议。

dev4u commented 3 years ago

这个机制和OTA有什么不同吗?

RPRX commented 3 years ago

@dev4u 完全不是同一个东西

RPRX commented 3 years ago

看起来若要实现这个机制,明文 TCP 代理协议结构只需一处小改动:返回的数据前加一个字节代表新密钥长度,0 代表无新密钥

2021 年了,只用 AEAD 吧

各方面细节问题已经考虑清楚,有空时我将先用 Xray-core 实现它们,进行一些初步的试验

RPRX commented 3 years ago

如果没什么问题,此方案可以成为 SIP010,作为 SIP004 和 SIP007 的延续。SIP008 可以提供相应的 API 支持。

felixding commented 3 years ago

这个 @fortuna 应该会感兴趣,Outline好像也在考虑前向安全的问题。

zonyitoo commented 3 years ago

此方案存在几点细节:

  1. 如何区分客户端?此方案相当于每一个客户端与服务器之间建立唯一的Session,目前的机制无法准确识别每一个单一客户端实例。存在几种可能的方案:

    • 增加SessionID标识,每次新建立请求时跟着IV / SALT请求到服务端。或者为ClientID, UserID,本质相同
    • 按端口标识,即Client与Server严格保持一对一关系
  2. 不需要在加密方式前增加FS_前缀,此方案没有修改加密方式,而是协议层面有修改,可有几种方式

    • 配置文件增加 protocol_fs: true
    • 或增加协议版本号 protocol: aead_fs
  3. 通讯协议具体细节未定义

    • 若涉及协议修改,可考虑定义为可变长Header结构,方便后续增加新字段
    • 按当前协议描述,新密钥增加在服务端返回的 IV / SALT 之后
      +--------+--------+--------+--------+
      |  IV / SALT      | EXT_LENGTH      | <- FIXED HEADER
      +-----------------+-----------------+
      | EXT_TY | EXT_VALUE                | <- VARIABLE HEADER
      +-----------------------------------+
      | ... DATA STREAM                   | 
RPRX commented 3 years ago

@zonyitoo

  1. 似乎不需要额外的机制,因为核心目的不是限制设备数量,而且,若多个客户端共享初始密钥,很快就会只剩一个能连上
  2. 确实更合理
  3. 这个。。。如果需要可扩展性,甚至可以直接插 PB,比如 VLESS(协议结构可扩展性极强)
zonyitoo commented 3 years ago

似乎不需要额外的机制,因为核心目的不是限制设备数量,而且,若多个客户端共享初始密钥,很快就会只剩一个能连上

如果我确实需要实现多设备使用的话要如何实现呢?服务端根据密钥来区分Client么?

RPRX commented 3 years ago

@zonyitoo

是的,需要生成多个初始密钥,如果同端口则需要进行解密尝试,但由于只判断第一个包,所以代价很低

zonyitoo commented 3 years ago

多加几个字段是否可以让各实现之间歧义更少?服务端也可以更明确知道具体是什么client在连接。

RPRX commented 3 years ago

@zonyitoo

我觉得按照规范,独立密钥足以同时作为认证机制了?

RPRX commented 3 years ago

@Mygod 说出你的想法

RPRX commented 3 years ago

@QuantumGhost 又来一个

QuantumGhost commented 3 years ago

我觉得:

  1. Shadowsocks 的主要目的不是保证流量安全,而是保证无法识别,翻墙不受阻碍
  2. 上述机制对于实现 1 并无帮助,而且相对来说较为复杂
  3. Shadowsocks 有插件机制(SIP003),如果有实验性的想法可以通过插件来进行验证
zonyitoo commented 3 years ago

Implement it with SIP003, that's quite a good idea.

mzz2017 commented 3 years ago
  1. 我认为后向安全性没办法得到保证,攻击者拿到当前秘钥后很容易“跟上步伐”,因为在绝大多数情况下我们默认攻击者有能力记录所有包。
  2. 另外,该方案还是无法彻底解决“云上贵州”问题,同问题1。
RPRX commented 3 years ago

@QuantumGhost

  1. 我很难赞同你的观点,或许你看看这里 https://github.com/shadowsocks/shadowsocks-windows/issues/3059
  2. 就我个人而言,缺乏前向安全的东西是不敢用的,不希望通信内容哪天被拉清单
  3. Shadowsocks 同端口未知流量就是很明显的特征,不如先把这个改掉?
  4. 注意我公开的想法都不是实验性的,而是一定会实现的,只是看 SS 组织有无意愿一起标准化
  5. 所以如果无意愿,我更乐意直接实现一个 Secure Shadowsocks,并向人们普及一下什么叫前向安全
  6. 可能你并不知道过几天会有一篇文章出来,而文中的机制可以解决问题

话说回来,若只是为了翻墙,如果你不介意买个域名,XTLS 可能是现阶段更好的选择。

RPRX commented 3 years ago

@mzz2017

事实上后向安全是很难得到保证的,TLS 也做不到这一点,但动态更新的密钥为后向安全提供了基本的可能性,这一点比 TLS 强

QuantumGhost commented 3 years ago

@rprx YOMV

另外补充一点,

由于现在的国产安卓系统都有云备份功能,手机上的 SS 密码没有安全保证,它可以被用来解密一切通讯内容。

如果你要解决终端不受信任的问题,这一些变更远远不够(这是一个非常非常困难的问题,如果我是你我就不会在这上面花时间,除非我在攻读相关领域的博士)

RPRX commented 3 years ago

@QuantumGhost

这个问题很难解决,我只是描述了一个尴尬的事实:由于缺乏前向安全,在国产手机上使用现有的 SS 是风险极高的行为

QuantumGhost commented 3 years ago

就算有密钥协商,系统依然可以记录你协商得到的短期密钥,所以这些方案并不能确保前向安全

我个人认为,如果你不信任你的手机 / 系统,除了更换设备之外并没有其他解决方案

RPRX commented 3 years ago

@QuantumGhost

注意云备份是定期备份,不是实时监控,就这一点而言,TLS 方案比 SS 方案更安全

RPRX commented 3 years ago

或者我这么说,假设你一开始是在电脑上使用 SS,此时只要你电脑不丢,就是相对安全的

某一天你想在手机上用 SS,扫个二维码,晚上系统给你云端一下,boom,连你电脑的 SS 也跟着不安全了,甚至包括历史通讯

wevsty commented 3 years ago

1、SS的设计目的主要是防止识别,并不是保证流量安全性。

2、SS本身虽然无法保证向前安全,但是用户在访问各种网站的时候仍然会受到TLS保护。由于TLS本身具备了向前安全的保障,所以即使SS流量能被解密也不会被随便窥探到流量内容。

题外话: 基于安全原则,我们不应该信任任何运营商提供的基础设施。 事实上,即使SS提供了向前安全,我们仍然需要TLS来保护流量。既然如此,SS是否提供向前安全这个问题就并不是那么重要了。

3、如果终端无法被信任,那么这个终端上发生的一切同样都不应该信任。

4、为不同客户端随机更换密钥的方案我不认为是一个好方案。 理由 4.1、由于密钥要求是定时或者不定时更换,服务端无法确定客户端用什么密钥来连接服务器。所以为了能响应客户端的访问,服务端必须尝试所有有效的密钥来解密或者采用明文通知的方式。 如果必须尝试所有有效密钥,那么客户端数量多了以后绝对是一个灾难。 如果是采用类似明文来通知的方式,显然是增加了特征,违背了设计初衷。 4.2、如果假设终端会上传客户端保存的密钥,那么如何保证上传的密钥不会保存N个版本?我认为是没办法保证的。 如果保存了N个版本那更换密钥与不更换本身是没有区别的。

RPRX commented 3 years ago

@wevsty

  1. 对我而言,在稳定 FQ 方面,XTLS 远比 SS 更合适且高效,但 SS 可以是一个不强依赖第三方的可信隧道方案,即特定客户端与服务器间的无特征可信隧道。况且,无论用途,安全性始终很重要,若能做到更安全,为何不做?不能不思进取。
  2. 即使承载的大部分流量是 TLS,它也并远未普及 ESNI/ECH,相信你知道我在说什么。此外,SS 可被破解意味着可被实锤。
  3. 这个说法非黑即白了,终端并不是完全无法被信任,只是现代的云备份会对 SS 静态密码造成致命打击。
  4. 建议重新看一遍我描述的方案。另外,即使你非要同端口多用户,也只需要判断第一个包,成本极低。

说一下前向安全:

  1. 一般指的是密钥的第一次泄露,比如密钥第一次被手机泄露时,电脑的历史通讯还是不可破解的、安全的。
  2. 当然对于我的方案,上述场景压根就不存在,因为不同设备间必须是独立的。
  3. 事实上随着你的使用,动态密钥一天内就会被更新 N 次,即使每天快照一次,也远比现在的静态密码强,至少增加了跟踪难度,而不是随便拿到一段此前或此后的流量就可以直接解密。
RPRX commented 3 years ago

没救了

dev4u commented 3 years ago

@rprx 我问跟OTA有什么区别,意思是我认为你这种想法,会为ss的通讯带来了特征和爆破点。

特征不多说了。

引入的爆破点在于,客户端怎么知道,返回的密钥是中间人告诉他的?还是真实服务端告诉他? 此处是不是应该有个主密钥,来验证这个信息?主密钥从哪来……是不是又回到ss的原点? 我承认ss仍有不足的地方,但我相信这一切都会好起来的。

RPRX commented 3 years ago

@dev4u

所以,会带来什么特征?

初始密钥由 SSH/TLS 可信通道传输,而新的密钥是 AEAD 解密出来的,又有什么问题?

dev4u commented 3 years ago

@dev4u

所以,会带来什么特征?

初始密钥由 SSH/TLS 可信通道传输,而新的密钥是 AEAD 解密出来的,又有什么问题?

如你前面所说: “若要实现这个机制,明文 TCP 代理协议结构只需一处小改动:返回的数据前加一个字节代表新密钥长度,0 代表无新密钥” “由于只判断第一个包,所以代价很低”

有条金玉良言:绝不要让密码在“空中”传播。

RPRX commented 3 years ago

@dev4u (顺便 @wevsty

存在一些误解,我 kindly 说明一下

若要实现这个机制,明文 TCP 代理协议结构只需一处小改动:返回的数据前加一个字节代表新密钥长度,0 代表无新密钥

这里指的是服务端 返回 数据时的 明文 协议结构,是还没有被加密的

由于只判断第一个包,所以代价很低

这里指的是服务端 接收 请求时,只需尝试解密第一个 packet 即可找出同源后续大量数据的所属用户,实现低成本端口复用

wevsty commented 3 years ago

由于只判断第一个包,所以代价很低 这里指的是服务端 接收 请求时,只需尝试解密第一个 packet 即可找出同源后续大量数据的所属用户,实现低成本端口复用

首先TCP是流协议,在应用层上不存在包这种概念。不过,不纠结,就当作所谓一个包是N字节的数据块好了。

对于开销的问题下面做个简单的算术题: 假设一个SS服务端有100位用户,每位用户均只有1个设备,按照你的方案服务端就需要储存100个密钥。 攻击者发出一个请求(假设请求为1个16 bytes 的 AEAD block),SS服务端需要尝试每一个密钥来确定用户是否为合法用户,也就是100次AEAD运算。 我这里有一台AMD Ryzen 3600的机器,运行 openssl 测试的结果如下:

root@ryzen:~# openssl speed -evp chacha20-poly1305
Doing chacha20-poly1305 for 3s on 16 size blocks: 58503757 chacha20-poly1305's in 3.00s

单核情况下3秒处理了 58503757 个块,也就是大约 19501252 blocks/s 。 一个请求因为需要尝试100次,所以也就是需要至少100个块来处理,也就是大约 195012 请求/s 。 请求数换算成流量就是 195012 16 bytes/s = 3120200 bytes/s = 3120.2 Kilobyte/s = 3.1202 Megabyte/s Ryzen 3600 有6个物理核心,12个逻辑核心,我就当作是满血的12核心好了,也就是最大能处理 3.1202 12 = 37.4424 Megabyte/s.

从计算结果来看,基于有100个合法终端的情况下,最多只需要 37 MB/S 就能打死我这台Ryzen 3600的服务器。

如果合法的终端越多,处理能力还会更加低下。 所以说,这种做法运行代价很低么?反正至少说服不了我。

我的意见仍然是如果需要保证流量向前安全,请给流量套一层TLS,这样做的代价更低,安全更有保障。

RPRX commented 3 years ago

@wevsty

答不对题,我描述的方案又不是强制 SS 端口复用。裸 TCP 协议端口复用就是这样的,并不是非复用不可。

RPRX commented 3 years ago

@wevsty

还有,我仔细看了一下,你描述的方法有明显问题。

按你的数据,即使是 100 用户同端口,AEAD 并发 37 MB/s 大概需要攻击者有 (37*1024^2)/16 = 2,424,832 个公网端口。

RPRX commented 3 years ago

鉴于上面有几位根本不懂协议设计或工程实践,Reopen 以跟进 SS 后续进展。

RPRX commented 3 years ago

@wevsty

尤其是这位错误理解后的错误回复带歪了楼,误导了他人的判断。

wevsty commented 3 years ago

@wevsty 尤其是这位错误理解后的错误回复带歪了楼,误导了他人的判断。

嘛,就当作是这样吧。

反正我的观点已经表达的很清楚了。 我的建议仍然是:如果需要保证流量向前安全,请给流量套一层TLS,这样做的代价更低,安全更有保障。

RPRX commented 3 years ago

@wevsty

不好意思,基于错误认知推导出来的是错误的观点。

从我的角度出发,我已经写了 XTLS,无论是安全、隐蔽、性能都远超 SS,现在我只是在设计一个备用方案。

fortuna commented 3 years ago

There's a very simple way to implement forward-secrecy to Shadowsocks that is mostly backwards-compatible with existing clients (may need minor tweaks). I want to add that mechanism to the Outline VPN.

My proposal relies on Online Configs (SIP008) and I mentioned it at https://github.com/shadowsocks/shadowsocks-org/issues/89#issuecomment-725866529.

Basically:

You get forward secrecy because the session secret is obtained over HTTP, which guarantees the secrecy. This mechanism has the added benefit to allow the server admin to seamlessly migrate clients to new servers.

We are adding online configs to Outline. The shadowsocks-android and shadowsocks-win clients already support online configs. I'm not sure if they fetch on every connection though, and they certainly don't support TTLs.

@felixding Thanks for cc'ing me /cc @alalamav

fortuna commented 3 years ago

I'd like to make a small correction to the initial post of this thread: there are many protocols that use the same port for TCP and UDP, including FTP, SSH, DNS and TLS/QUIC: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml Shadowsocks doesn't restrict the use of different ports. That should be possible to do, but that should be discussed separately.

fortuna commented 3 years ago

In response to @wevsty in https://github.com/shadowsocks/shadowsocks-org/issues/177#issuecomment-753818273:

Outline has been using trial decryption to support multiple keys on the same port since 2018: https://github.com/shadowsocks/shadowsocks-org/issues/130#issuecomment-442873714

It is not an issue to try to decrypt for every client, if you optimize it well (time and allocations!), which took significant work on our part. Besides code optimizations, we also keep the keys sorted but most recently used and have an IP -> key map.

We've seen multiple servers in production with over 400 keys and less than 0.1ms to find the key for at least 99% of the connections.

database64128 commented 3 years ago

and they certainly don't support TTLs.

If Outline plans to implement TTLs for SIP008, shadowsocks-windows will add support.

RPRX commented 3 years ago

@fortuna

抱歉我习惯使用中文,让我们先谈论一下这个问题

I'd like to make a small correction to the initial post of this thread: there are many protocols that use the same port for TCP and UDP, including FTP, SSH, DNS and TLS/QUIC: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml Shadowsocks doesn't restrict the use of different ports. That should be possible to do, but that should be discussed separately.

注意现在 SS 的特征并不仅仅是 TCP、UDP 同端口这么简单,它还无法被识别为任何已知流量,所以使用不同的端口是更好的选择

RPRX commented 3 years ago

@fortuna

然后我来说一下通过 TLS 更新密钥这个反复被提及的方式

首先,当然可以这么做,最简单的方法就是定期通过 TLS 等可信通道来更新密钥,没有问题 但这里需要区分两类使用者:中心化 VPN 提供商(俗称机场)和自建用户 对于前者,本来就拥有 TLS 通道,可以定期刷新用户的订阅,通过换密钥来实现相对安全

而对于后者,这里是 SS 用户,如果需要 TLS 的参与,即需要在服务器上部署 TLS,那么,有什么理由不直接用基于 TLS 的代理?一个自建用户,为了实现 FS,去服务器上部署个 TLS,却回来用 SS?这显然是不现实的。就国内的情况而言,目前基于 TLS 的代理具有更好的抗封锁性,还在用 SS 有多种原因,比如不想买域名、更方便些,而我描述的方案就是不强依赖 TLS 的实现。

所以说,对于 Outline,以及其他中心化 VPN 提供商,定期刷新密钥可行,但对于国内自建用户,SS、TLS 基本上是二选一。

此外,感兴趣可以了解一下我写的 XTLS,就现有的 Cross-wall 用途而言,它比 SS 更安全、隐蔽,且有更好的性能。

fortuna commented 3 years ago

There are many reasons why SS is still used, such as not wanting to buy a domain name, which is more convenient

You don't need to buy a domain in order to support TLS. You can use a self-signed certificate and share a fingerprint. That's how we do in the Outline Manager. The app manages the server via a REST API over HTTPS.

The clients that support online configs would have to support certificate fingerprints or some other validation (e.g. show the cert to the user and ask for confirmation).

One thing that makes my proposal more practical is that it's built on top of the protocol. There's no protocol change needed. The modularity and reuse of existing components makes it a lot simpler and more feasible to deploy.

RPRX commented 3 years ago

@fortuna

对于抗审查来说,使用自签证书,或者其它额外的方式来定期更新密钥,广泛应用后很可能会是强特征。

事实上我是在推动一种“配置无感热更新”的机制,即无需通过别的途径,使用过程中就可以自动完成配置更新,非常精巧。

更新的内容可以是临时密钥,以实现前向安全、防重放等潜在攻击(比如几天后将揭露的一种针对服务端的攻击方式可以通过此机制来缓解),也可以是其它的东西,比如 Port,如果你研究过 VMess 协议的话。

更重要的是,这或许是下一代 Shadowsocks 协议可以参考的设计。

fortuna commented 3 years ago

For anti-censorship, the use of self-signed certificates or other additional methods to periodically update the key is likely to be a strong feature after being widely used.

@rprx I would love to see evidences of that. Outline uses self-signed certificates and the evidence I have so far says that it's not used to detect servers.

Just because something can be done it doesn't mean it's done or will be done. And you gotta take into account the cost and collateral damage:

wevsty commented 3 years ago

In response to @wevsty in #177 (comment):

Outline has been using trial decryption to support multiple keys on the same port since 2018: #130 (comment)

It is not an issue to try to decrypt for every client, if you optimize it well (time and allocations!), which took significant work on our part. Besides code optimizations, we also keep the keys sorted but most recently used and have an IP -> key map.

We've seen multiple servers in production with over 400 keys and less than 0.1ms to find the key for at least 99% of the connections.

This is an interesting practice. Under normal circumstances, we can optimize to speed up access for regular users, which seems to perform well. But I can't say for sure that the server will be more likely to go down if it encounters a deliberate attack.

According to my calculations, based on the assumption of 100 keys, if it is running on an average VPS (single core cpu), it only takes about 25 Mbit/s of malicious traffic to cause a CPU bottleneck in the event of a malicious attack. This means that the service will be more vulnerable to DDOS attacks.

I think my calculations should be close to the theoretical maximum acceptable speed. Because I only calculated the runtime overhead required for encryption. I think this is a question worth discussing.

fortuna commented 3 years ago

@wevsty You are right about the server being more vulnerable to DoS attacks, however we haven't seen this being a problem in practice. The DoS attack is not a crippling threat: as soon as the attack stops, the service is restored. Because the attack has to be sustained, you can mitigate by throttling or banning IPs.

RPRX commented 3 years ago

Closed.

既然需要改动现有的协议结构,不如从头设计一个全新的方案,更加彻底地解决现有的、潜在的各种问题。

https://github.com/shadowsocks/shadowsocks-org/issues/178 是不需要改动现有协议结构的,同时同样不需要长期的 TLS,现阶段来说是个更合适的方案。

RPRX commented 3 years ago

作为一个可以被参考的前向安全方案,Reopen.