安全 模型。
什么在保护一张纸条、服务器能看到和看不到什么、边界在哪里,我们直言相告 — 因为你无法理解的安全并非安全。
密码学
- 加密: 每张纸条在上传任何内容之前,都会在你的手机上用 AES-256-GCM(12 字节随机数和 16 字节认证标签)封存。被篡改的密文会认证失败,而不是解密成乱码。
- 密钥派生: 密钥来自对 32 个新随机字节 — 即片段密钥 — 加上一个 16 字节随机盐的 HKDF-SHA256。这 32 个字节只进入链接的 #片段,别无他处。
- 口令(可选): 如果你设置了口令,它会用 PBKDF2-HMAC-SHA256 迭代 60 万次强化并混入派生,因此仅凭链接已不足够。错误的尝试会在收件人的浏览器中本地失败,绝不消耗查看次数。
- 双重验证: 应用中的 Swift 和 Kotlin 实现与查看器中的 WebCrypto 实现通过独立生成的测试向量相互校验,因此由一方封存的纸条总能在另一方打开。
片段密钥链接
一个纸条链接看起来像 burnpony.app/n/<id>#<key>。# 之后的一切都是 URL 片段,浏览器不会在 HTTP 请求中发送片段 — 无论是发给本服务器还是任何其他服务器。服务器得知的是随机的纸条 ID;密钥只存在于链接本身以及收件人阅读时的浏览器中。这就是为什么「服务器无法读取你的纸条」是网络工作方式的一种属性,而非一个承诺。
服务器存储什么 — 又无法读取什么
当一张纸条存活时,服务器持有:随机纸条 ID、封存的密文数据块、创建与到期时间戳、查看额度与计数、是否请求了已读回执,以及可让你的应用提前焚毁纸条的管理令牌的哈希。若开启回执,则记录打开时间戳。这就是全部清单。它从不持有密钥、纸条正文,甚至自动隐藏设置 — 后者以加密形式随纸条传递。创建限流使用带服务器密钥的 IP 哈希,约两小时后过期,而非原始地址日志。
焚毁,以及留下什么
当最后一次允许的查看被取走时,密文会在提供它的同一个数据库事务中被删除。过期的纸条由每五分钟运行一次的清扫移除。至关重要的是,已焚毁的纸条、已过期的纸条和从未存在的纸条都返回完全相同的响应 — 服务器无法被当作预言机来确认某个链接是否曾经真实。
查看器
收件人打开单一自包含页面:无框架、无 Cookie、无分析,除了向本服务器请求封存的纸条本身外不向任何地方发起请求。它带有严格的 Content-Security-Policy(default-src 'none')、noindex 指令,以及 no-referrer 策略,使链接绝不会通过 referrer 头泄露。解密在浏览器内置的 WebCrypto 中进行。页面短到足以用「查看源代码」审计 — 这是设计目标,而非偶然。
已读回执,坦诚而言
回执按纸条选择启用。收件人在显示之前会看到「打开此纸条时将通知发件人」。你收到的推送刻意笼统 — 「一张纸条已被打开。」仅附带纸条 ID — 因此纸条内容不会经过推送提供商的服务器。
威胁模型
BurnPony 保护纸条的机密性和完整性,抵御服务器、网络观察者,以及日后拿到不含片段的 URL 的任何人 — 并限制纸条究竟能被读多久、多少次。它假设你的手机和收件人的浏览器按文档行事。它不能防御任一端点上的恶意软件,也不能防御在纸条可见时对其复制、截图或拍照的收件人 — 没有任何阅后即焚工具能做到。自毁限制的是未来的访问;它无法管束阅读的那一刻。
诚实的限制
- 链接即密钥。 任何人只要在纸条焚毁前拿到完整链接(若设置了口令,还需口令),就能读取纸条。请通过你愿意托付该秘密本身的渠道发送,或添加口令并单独分享。
- 屏幕可被截取。 自动隐藏倒计时减少了肩窥的时间;它并非防截图保护。
- 丢了手机,就失去管理。 你已发送的纸条仍会按其自身安排焚毁和过期,但没有应用的「已发送」标签,你就失去了提前焚毁或查看状态的能力。
- 存在网络层面的元数据。 像任何 Web 服务器一样,这台服务器在处理请求时会在网络层面看到连入的 IP 地址。中继前端的 Web 服务器已配置为禁用访问日志,应用只保留上文所述的短生命周期加盐 IP 哈希。
确切规范
本页的每一项主张都在 v1 协议页面 上逐个常量地精确陈述,依据 公开仓库 中的冻结代码撰写,并可对照共享测试向量核查。
开放审查
查看器如今在每个纸条链接上都可审计,加密核心、查看器和中继服务器均计划公开发布 — 进展见 开源页面。
报告漏洞
发现了什么?发邮件到 NorseHorse@norsehor.se — 如愿意可用 PGP 密钥 加密。报告直达开发者,且因为中间没有委员会,修复上线很快。