原始插件说明
| 摘要 | Discourse Encrypt 支持用户之间进行私密的加密消息传递。所有敏感信息都安全地存储在服务器上,并且仅在客户端进行加密和解密。 | |
| 仓库链接 | https://github.com/discourse/discourse-encrypt | |
| 安装指南 | 如何在 Discourse 中安装插件 |
使用此插件的三个简单步骤
- 启用加密并激活当前设备。
- 发送加密消息。收件人也必须已启用加密。
此外,您还可以设定整个消息或特定帖子被永久销毁的时间。
- 阅读秘密消息。必须先激活加密才能阅读。
注意:在此示例中,由于加密已停用(通过注销或从偏好设置屏幕明确停用),系统再次提示用户输入纸质密钥。
技术信息
该插件通过端到端加密方案,使用户能够通过 Discourse 进行安全通信。插件的大部分逻辑在客户端实现,服务器端仅处理公开或加密的信息。它不加密任何帖子元数据,例如对话参与者的姓名、发布时间、点赞、小型操作等;上传的内容是加密的,但其存在性不会被加密,因为系统必须将上传内容与帖子关联以防止删除它们。
整个代码都是 开源 的,欢迎安全爱好者进行审查。如有任何进一步的信息,请随时联系我或团队。 ![]()
阅读更多...
摘要
该插件的目标是提供加密内容的完整性和机密性,并防止信息泄露和未经授权的访问。以下章节描述了常规操作模式、使用的算法和威胁模型。
要使用此系统,用户只需注册一次,生成由两个 4096 位 RSA 密钥组成的“用户身份”,一个用于加密,另一个用于签名。用户可以导出其“身份”以便安全保管,或者在生成纸质密钥后将其加密并存储在服务器上。这两种方法可作为备份,或用于注册新设备。
纸质密钥(灵感来源于 RFC 1751 和 BIP-39)是人类可读的密钥,用于将“用户身份”安全地存储在服务器上。纸质密钥由从 2048 个单词列表中随机选取的 12 个单词组成,提供 121 位的熵(第一个单词用作标签)。要使用纸质密钥加密“用户身份”,系统将首先使用 PBKDF2 派生加密密钥,将纸质密钥 拉伸 为 256 位 AES-GCM 密钥。
创建加密帖子
要创建新帖子,用户(其浏览器)将:
-
使用其私钥对当前帖子内容进行签名;
-
生成一个新的“主题密钥”(一个 AES-256-GCM 密钥)- 该密钥将用于加密帖子、部分帖子元数据和新主题的标题(如果可用);
-
获取所有参与者的公钥,并为每个人加密“主题密钥”;
加密操作的伪代码如下:
signature = rsa_pss_sign(current_user.identity.sign_key.private, post.raw) # 1
topic_key = topic.key || generate_aes_256_gcm_key() # 2
encrypted_title = aes_256_gcm_encrypt(topic.title, topic_key) if topic.blank?
encrypted_post = aes_256_gcm_encrypt(signature + post.raw, topic_key)
encrypted_topic_keys = recipients.map { |r| rsa_oaep_encrypt(topic_key, r.identity.encryption_key.public) } # 3
$.put("/posts/create", { title: encrypted_title, raw: encrypted_post, keys: encrypted_topic_keys }) # 4
阅读加密帖子
要阅读帖子,用户(其浏览器)将:
-
获取加密的帖子负载(帖子明文和签名)以及加密的主题密钥;
-
使用其私钥解密加密的主题密钥;
-
使用解密后的主题密钥解密加密的帖子负载;
-
获取发帖人的公钥并验证帖子签名。
算法套件
该插件广泛使用了 Web Crypto API 中实现的加密原语,这些原语在 Discourse 支持的 任何现代浏览器(Internet Explorer 除外)中均可用。
-
getRandomValuesPRNG:用于生成纸质密钥和 96 位随机 IV。 -
PBKDF2:将 132 位纸质密钥拉伸为 256 位密钥,用于加密“用户身份”。
-
AES-256-GCM:用于加密每个帖子的内容。它还通过生成 128 位的认证标签提供认证功能,但这方面的作用较小,因为帖子是通过发帖人生成的签名进行验证的(见上文步骤 1)。
-
RSA-OAEP:用于加密“主题密钥”和“用户身份”以便在服务器上安全存储。所有 RSA-OAEP 密钥长度均为 4096 位。
-
RSA-PSS:用于对每个帖子的内容进行签名以验证真实性。所有 RSA-PSS 密钥长度均为 4096 位。
原语
该系统使用了一组基于浏览器通过 Web Cryptography API 提供的原语构建的原语。
-
encrypt 和 decrypt:用于加密和解密帖子内容。
encrypt接收一个 JSON、一个 AES-256-GCM 密钥和一个 RSA-PSS 公钥,输出单个 Base64 编码字符串;decrypt接收一个 Base64 编码字符串和一个 AES-256-GCM 密钥,输出初始 JSON 对象; -
verify:用于在解密后验证帖子内容;
-
exportIdentity 和 importIdentity:用于导出和导入“用户身份”。
密钥类型:
-
主题密钥 (AES-256-GCM)
- 用于加密主题中的每个帖子(帖子是单独加密的)
- 由原始发帖人在客户端通过 WebCrypto 的 API
generateKey原语 为每个主题 生成 - 使用 WebCrypto 的 API
wrapKey原语 用每个有权访问该特定主题的用户公钥进行 加密 - 在服务器端为每个用户 存储(加密后)在
PluginStore中
-
RSA 密钥对(公钥和私钥) (RSA-OAEP 和 RSA-PSS, 4096 位)
- 用于加密用户有权访问的所有主题密钥
- 由原始发帖人在客户端通过 WebCrypto 的 API
generateKey原语 为每个用户 生成,并在用户的所有设备间共享 - 服务器端:公钥身份按客户端导出的方式存储,但私钥身份将始终使用密码短语密钥进行加密
- 客户端:公钥和私钥作为
CryptoKey存储在 IndexedDb 中;如果不可用,则使用window.localStorage(在 Safari 中)
-
密码短语密钥(使用 PKBDF2 派生,迭代 128,000 次)
- 用于加密“用户身份”以便在服务器上安全存储
- 从纸质密钥(或出于遗留目的的用户密码短语)派生
威胁模型
被攻陷的 Discourse 实例
攻击者如果注入代码,理论上可以通过提供恶意代码来访问加密信息,该代码解密加密内容并将明文帖子发送到另一台服务器。要实现这一点,只需拥有管理员账户并创建一个包含恶意代码的主题组件即可。
默认的保护机制(如 CSP)可以检测和缓解 跨站脚本 (XSS) 攻击,这也可能是注入恶意代码的一种方式。
中间人攻击
在 中间人攻击 中,攻击者拦截用户与服务器之间的通信,从而能够读取或篡改它。由于插件在发送前加密所有内容,攻击者无法仅通过窃听来解密任何内容。同样,由于信息经过认证,攻击者也无法篡改它。
然而,攻击者可能会向用户返回恶意代码,并遵循与前一小节中描述的类似的攻击。HTTPS 在一定程度上缓解了这种情况,它大大降低了攻击概率。
备注
该插件已有一定的历史,浏览源代码并注意到协议 v0(初始,alpha-beta 版本)和 v1 的两种实现即可看出。协议 v0 不再用于加密新帖子,但保留用于继续解密旧帖子。新协议包括密文的真实性,所有帖子均使用发帖人的私钥进行签名。
