挖一个关于 IM 工具的新坑
引言
我考虑重新设计一个即时通讯工具的想法已经很久了,但一直没有付诸于事实。有几个理由让我不得不设计一个新的 IM。我对于我目前正在使用的 IM 工具都不能感到满意。我基本可以把我正在使用的 IM 分成几类:
- 由运营商直接提供的通讯系统(电话、短信)
- 社交属性重的通讯工具(微信、Line、Facebook Messenger)
- 具有一定安全性设计的通讯系统(iMessage、Telegram、Keybase、Signal)
产品调研
第一类工具是同时缺乏产品性和安全性的,它们的功能基础而单一,随便一点更新都很难推进,连 VoLTE 都是一个普及了好几年都很少有人在用的情况。而像是可以进行多人群聊的语音会议或是群发短信操作都非常复杂,资费同时也非常高。更严重的问题是,这些产品对于安全性是几乎没有保护的,你的聊天内容对于运营商不但是明文的,随便给你来个 GSM 伪基站配合降级攻击很容易就能让不法分子有可乘之机。
第二类工具的产品性非常强,功能迭代速度很快,你想要的东西它都有。但同时你泄漏的用户资料更多,你甚至无法判断这到底是一个社交应用还是一个聊天应用。人在公开场合聊天和私下聊天的目的明显是不同的。另外,一旦这些厂商可以通过利用你的个人隐私来盈利时,它们很难不这么做,这是资本的力量。据小道消息称,微信的开发团队来自于原邮箱团队,设计上很大参考了邮箱的设计,一直到前几年通过引入了 MMTLS 协议来提供了安全性上的保障。但由于微信不是一个开源工具,其安全性无法通过白盒测试的方法进行验证,如果存在后门,用户是无法预防的。而一旦通过黑盒方法找到漏洞的靠着这个赚钱都来不及,就更不要说能及时得到修复了。
第三类有安全性设计的系统例如带有 E2E 加密设计的 iMessage Telegram Keybase 和 Signal。iMessage 的安全情况我个人是高度怀疑的,尤其在 AppleID 安全事故频发的情况下。Telegram 虽然支持 E2E 模式,但并不是默认开启的,对多客户端的支持也有问题。桌面端的一些实现甚至不支持 E2E 模式,同时也不支持群聊加密。Telegram 和 Signal 基于手机号码的形式有很大的被嗅探的风险,这也是我不选择的一个原因。至于 Keybase 基于的 GPG 系统按理说是安全的,但 Keybase 是一个半开源的状态,我非常不放心这样的设计。更大的问题是,Keybase 在尝试把这个隐私优先的 IM 工具做成一个非常社交化的系统,每个人都能很自由地查询到用户的各个设备具体公钥的元信息,在隐私上造成了很大的困扰。
产品设计
任何人的私生活、家庭、住宅和通信不得任意干涉,他的荣誉和名誉不得加以攻击。人人有权享受法律保护,以免受这种干涉或攻击。——《世界人权宣言》第十二条
如果一个系统是通讯系统,那么隐私是这个系统的第一要务。一个没有良好隐私设计的通讯系统如同乔治奥威尔的小说《1984》中无处不在的 电幕(Telescreen) 一样。更可怕的是,电幕本身的设计也许也很不良,以至于能观看到你隐私的不止有老大哥,还有可能是黑色产业链下的其他人。如果一个系统有后门,你不能保证知道这个后门的只有你自己。
这个系统同样必须要是毫无保留开源的,只有把安全问题曝光在太阳之下,才能及时解决这些问题。我希望这是一个自由软件(客户端基于 GPL,服务端基于 AGPL),这有助于每个人更好改进它,或者改造成适合在公司内使用的内部聊天工具。
基于这点之下,我重新设计了账户系统、聊天系统和群组系统。并期待能在隐私的大前提下去建立一个合适的聊天工具应有的样子。
账户体系
账户体系是围绕 GnuPG 设计的,服务器只存储你的公钥信息。如果你丢失了你的私钥或者保护私钥的密码,那么你的账户是完全不可找回的。当你的 key 发生泄漏的情况下,你可以通过吊销证书彻底摧毁掉这个帐号,不提供任何恢复的选项。这个过程直接粗暴,不会有所谓客服介入,以减少社会工程学攻击的可能。吊销前你可以通过没有丢失的设备导出好友列表,但之后依然需要在新帐号重新添加。
当用户在新设备上登录时,用户会生成一组设备密钥对,并用账户私钥加密存储。之后每次登录则是需要验证账户私钥然后将设备密钥对加载至内存中,关机即销毁。当你登录后,你会和服务器建立连接。当你的好友试图与你通讯时,需要获取你全部登录设备的公钥,将一条信息用自己的私钥签名并用你的全部公钥加密后发送到服务器上,服务器转发给你。这个过程中,服务器除了知道发送者和接收者分别是谁,除此之外的全部信息都无法知晓,以实现点对点加密。
你除了用户名和头像以外的全部个人信息,包括你的公钥列表都必须在好友添加验证通过后才会被其他人看到,以尽可能减少元信息的泄漏。用户可以在应用中通过便捷的方式导出全部公钥的二维码图像,以便于通过其它信道与发送者检查是否存在 MITM 攻击。
群组设计
不同于 Telegram,这个系统的群组聊天同样提供有效的点对点加密,只是实现的机理不太一样。进入群组后,每个人会产生一个群组密钥对,利用其它群员的设备公钥将自己的公钥加密并传输给其它人。每次发送消息时用自己的群组私钥加密并发送出去,其它人用之前收到的公钥进行解密。
这个过程对于服务器来说同样也是无法知晓的,服务器只负责转发信息。需要注意的一点是,一旦有群组成员被踢出群组,那么这个密钥交换过程必须重置,以避免有问题的服务器将信息继续发送给已经被踢出群的群员后,其依然可以解密的风险。
守护服务(Daemon Service)
这个设计的一个弊端是你无法同一个没有在线的用户聊天。这是显然的,服务器没有保存你的私钥,发送者也不知道你会用哪个设备查看这条消息。真的是「辛辛苦苦 30 年,一朝回到解放前」,给你一种在使用 NetMeeting 的感觉。
不过我能想到的一个解决方案是提供一个无头的客户端,允许用户挂在到服务器上 24h 运行以代收消息,其它客户端可以与这个无头客户端同步消息以达到不丢数据的目的。帮助非极客用户提供部署守护服务可以作为官方的一个增值服务以弥补服务器的开支。
技术栈与开源协议选型
暂时把这个项目定名字叫 Nyan Chat(猫聊(暂定))
- nyan-core GPLv3 协议。加密的核心实现,Rust 开发,以库的形式加载给其它组件。
- nyan-api AGPLv3 协议。API 实现,Ruby 开发,基于 Sinatra、Sequel 和 Ohm,提供一下元信息的交互,以 Redis 作数据交换为主,Postgres 为辅,可水平扩展。
- nyan-node AGPL v3 协议。服务端通讯节点实现,Elixir 开发。提供与用户终端的 Socket 连接,与 Redis 通讯快速验证一些元信息。节点间自由通讯,可水平扩展。
- nyan-desktop GPLv3 协议。目前优先开发的桌面客户端实现,使用 TypeScript 和 Electron。
- nyan-daemon AGPLv3 协议。Go 语言开发的守护服务。
开发时间表另定。欢迎有兴趣的人留言讨论具体细节。
最后分享一个我最近发现的 2016 年在卡杜甘音乐厅演出的《文明 4》主题曲 Baba Yetu 的 Live,非常震撼。