我们希望委托开发一款插件,实现终端用户之间的私密、加密消息通信。
该插件现已存在并得到官方支持,详见 Discourse Encrypt (deprecated)
目前,在线安全、私密且适合长文交流的渠道非常有限。现有的工具通常晦涩难懂,需要极高的技术技能才能使用。因此,大多数“私密”交流仅限于在 Telegram、Skype 或 WhatsApp 等平台上进行“短文本”沟通。此外,这种大规模通信模式由于基于闭源工具和通常也是闭源的协议,无法进行适当的审计。
我们希望构建一个高度易用且可审计的解决方案,通过专门的 Discourse 插件实现加密消息功能。
为实现这一目标,我们将遵循以下原则:
- 基于 Web Crypto API 构建解决方案。
- 服务器为每个用户存储一个加密的私钥和一个公钥。
- 服务器为私密消息中的每位参与者存储一个加密的会话密钥(使用终端用户的私钥进行加密)。
- 所有 Markdown 内容和标题均以原始加密形式存储(使用会话密钥加密)。
- 接受以下限制:服务器知道谁与谁交谈以及何时交谈(我们仅保护“内容”本身);接受此类内容无法被搜索的限制。
拟定的终端用户体验
Jane 进入她的用户页面并点击“启用加密消息”按钮。点击后,系统会提示她输入一个秘密短语,界面会明确说明:如果她忘记了该秘密,将永远无法再访问她的加密消息。
启用加密消息后,创建消息的界面中将出现一个新的 [ ] 复选框,文字为 [ ] 加密消息。仅当所有收件人都已启用“加密消息”且拥有公钥时,该选项才可点击。如果部分用户未启用,点击时将提示:“抱歉,部分参与者未启用加密消息”。
Jane 决定与 Pete 进行私密交谈,她将 Pete 添加到参与者列表,点击 加密消息 并向 Pete 发送消息。
Pete 会收到通知,告知 Jane 发送了一条加密消息。通知中没有标题,仅显示存在加密消息的信息,并包含指向该消息的链接(在邮件通知和网页通知中均如此)。
如果 Pete 使用的是当初启用加密消息的原始设备,他的秘密短语此前已输入,凭据已存储在 IndexedDB 中。如果 Pete 使用的是新设备,系统会提示他输入秘密短语以启用加密消息。
界面会清晰显示覆盖层或视觉提示,表明消息已加密。
一旦 Jane 和 Pete 开始通信,其中任何一方都可以邀请新用户加入对话,前提是该用户拥有公钥。
Pete 或 Jane 可随时更改秘密短语。更改后,私钥将使用新短语重新加密并发送至服务器。(V1 版本不允许更改实际的私钥,仅允许更改短语。)
技术细节
在任何情况下,非加密的私密对话内容或私密凭据都不会发送至服务器。对服务器的唯一信任在于:发送给客户端的 JavaScript 负载未被篡改。
公私钥对将 100% 在客户端生成,随后私钥将使用 AES 对称加密进行加密,再交由服务器安全存储。我们将使用随机生成的盐(存储于服务器)对短语进行扩展。密钥扩展将在客户端使用 PBKDF2(Web Crypto API 中可用)完成。公私钥对将以不可导出的 CryptoKey 对象形式存储在客户端的 IndexedDB 中。可导出的 CryptoKey 数据将尽快从内存中清除。
加密后的私钥和公钥将存储在用户的自定义字段中。仅 current_user 被允许读写加密后的私钥,所有登录用户均可读取所有用户的公钥。
会话密钥将在对话开始或邀请时于客户端生成,并使用对话中涉及的所有公钥进行加密。该数据将存储在专用表 (user_id, topic_id, encrypted_conversation_key) 中。该记录可由对话中的任何用户创建,但仅当 current_user == user_id 时方可读取。
需特别小心,确保绝不向服务器泄露未加密的数据,包括确保草稿不会以未加密形式存储。
为避免一大类错误,我们将为编辑器编辑和预览使用新的 DOM 元素(带新 ID)。当前的 .d-editor-input 将仅包含加密的数据块,传统预览功能将被禁用(即同时存在两个文本区域:加密的和未加密的)。加密操作将进行大量防抖处理,以避免性能问题。
对于 V1(以及本工作项的范围),我们可以接受移除某些功能,包括文件上传支持、加密内容的链接预览(oneboxing)以及其他复杂的边缘情况。
在渲染此类私密内容时,我们接受在运行时解密、生成并经由白名单过滤器处理 HTML。
文档要求
在实施之前,将创建一份非常详细的规范文档,包括网页线框图,最重要的是,详细的网络安全概述。
测试要求
该插件必须包含一套全面的基于客户端和服务器的单元测试/集成测试。如果您承接此项目,请尽早编写测试,切勿等到项目结束才补写测试。
关注点
https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/ 中提到的许多担忧,鉴于 Web Crypto API(真正的随机字节生成、私钥存储等),已不再相关。目前存在的主要攻击向量是服务器向客户端发送恶意的 JavaScript。短期内,用户将被期望信任服务器,就像他们信任 1Password、LastPass 和其他 Web 安全 UI 一样。长期来看(V3/V4 - 明年或之后),我们可以考虑发布一个浏览器扩展,在所有 JS 负载的稳定性哈希值已知的网站上显示挂锁图标。
WebCrypto API 虽然相对较新,但已得到广泛支持,该插件应能在我们支持的所有浏览器上运行。
简单的粗略线框图
* 开始消息时,用户可以选择加密对话,如果任何参与者缺少公钥,将显示警告。
* 用户可以通过点击该按钮并经过生成密钥对的界面来启用加密消息。
如果启用了加密消息,界面应简单显示“您已启用加密消息”。
* 某种图标或覆盖层将标明消息已加密。
预算
这是一项构建非常复杂、需要深度集成和仔细审查的工作。我们期望该插件经过极其完善的测试(包括客户端和服务器端测试)。我们目前对 MVP(最小可行性产品)的预算为 10,000 美元。


