qub 安全

生效日期: 2026年9月23日 版本: 1.1 — 实施准确性审查


供研究人员参考 — 快速指南:

完整详情见第12条(协调披露)。


我们是谁

qub.social 由 VSPRY AUSTRALIA PTY LIMITED(ABN 41 631 026 330)运营,地址:Level 38, 71 Eagle Street, Brisbane QLD 4000, Australia。文中提到的"qub"、"我们"、"我们的"指代该实体。

安全联系方式:support@qub.social,邮件主题前缀 [SECURITY]。


1. 我们的方法

qub 是信任基础设施。如果它不安全,本产品就毫无价值,所以安全不是一个特性——它是底座。本页以具体术语描述了我们如何保障我们的技术栈、你的数据,以及已封印内容的完整性。

随着网络上越来越多的内容由机器生成,可验证的时间承诺的价值也在增长。一个经过验证的存储交易或透明日志锚可以证明密文最迟在其区块时间之前就存在;封存的产物则单独证明内容完整性、drand 轮次绑定以及任何作者签名。保持这些声明的区分,是本页面所遵循的标准。

我们不会要求你信任我们。我们的设计目标,是把对我们的信任要求降到尽可能小;而在确实需要信任的地方,我们会清楚说明被信任的是什么、以及为什么。

三条原则驱动着每一个设计决策:


2. 威胁模型

2.1 我们防护什么

2.2 我们无法防护什么

我们坦诚说明我们的局限。qub 无法防御:


3. 客户端密码学

在默认的浏览器消息流程中,内容加密发生在上传请求之前。有两条明确的路径不同:Builder /api/v1/seal 故意将明文和调用者生成的 K 发送给 Worker 进行内存封装,而 pact 阶段/协签将已签名的结构化 pact 发送到服务,以便它可以完成双边产物。两者都不应被误认为是浏览器路径的端到端加密。

3.1 时间锁加密

qub 使用 tlock —— 一种以未来 drand 信标轮为密钥的基于身份的加密。加密在你的浏览器中使用 drand 网络的公钥进行;解密密钥仅在到达目标轮时由 drand 网络公开释放。包括我们在内的任何人,都无法提前重建解密密钥。

我们以 quicknet 链为目标:

quicknet 链的公钥与创世时间被编译进客户端。我们不在运行时获取链参数,因此恶意节点无法替换为一条由我们控制的链。

3.2 对称加密

tlock 方案封装一个 AES-256-GCM 内容密钥。AES-GCM 提供经认证的加密:密文中翻转一个比特就会使解密失败,而非产生静默损坏的明文。

3.3 规范化序列化

协议结构使用确定性 CBOR(RFC 8949 §4.2 核心确定性编码)进行序列化。两个实现对相同的逻辑结构进行编码会产生相同的 CBOR。完整的封装负载是 不 确定性:tlock 和外部包装加密使用新的随机数。正文哈希是对原始正文字节计算的,而规范编码使周围的签名/传输结构明确无歧义。

为了让我们的客户端与服务端实现都保证一致,我们手写了 CBOR 编码器,而不是依赖通用序列化库——这里的要求是精确,而不是便利,并且两套实现都跑属性测试以确保它们彼此吻合。

一个回归测试断言:除了协议原语 qub_id 字段键之外,规范化线缆格式中不包含任何 qub 品牌字节序列。线缆格式有意做到品牌无关——任何符合规范的查看者(无论是我们的还是第三方的)都可以从永久存储渲染任意 qub,无关其由哪一个部署封印。该测试是一个绊线,防止未来的变更不慎将品牌引用烤进字节,而那些字节一旦进入永久存储就无法改写。

3.4 正文哈希与开启前完整性

每个封装的有效载荷都携带其原始主体字节的 SHA3-256 哈希。该哈希被绑定到 qub_id 并且,当启用作者签名时,会输入到 V2 签名输入中。查看者在解密后重新计算它,并拒绝不匹配的情况。

32字节内容标识符 qub_id 源自一个 108 字节的前映像,涵盖协议版本、内容类型、创建和解锁时间戳、可选的结果时间戳(或其零哨兵)、目标 drand 回合、主体哈希,以及可选 NFC 归一化标题的 SHA3-256。网关或 CDN 无法一致地更改任何绑定字段并仍通过重新派生。标题限制为 100 个 NFC 代码点,并且会拒绝共享敌对/控制代码点类(包括双向覆盖、零宽字符、标签块、BOM、C0、C1 和 DEL)。

3.5 签名(ML-DSA-65)

作者署名签名使用 ML-DSA-65(FIPS 204),一种由 NIST 标准化的后量子签名方案。我们刻意为签名选择了一个后量子原语,因为封印内容是永久的:今日能验证通过的签名,必须在数十年后——包括大规模量子计算机走向实用之后——仍能验证通过。

签名密钥在浏览器中生成。本地密钥在存储到 IndexedDB 前会被封装在一个不可导出的 WebCrypto 密钥下。如果使用基于账户的跨设备恢复功能,会在服务器端存储经过 AEAD 加密的可移植密钥块;其秘密密钥密文与不可变的账户 ID 绑定,服务端只验证公有封装,但无法解密秘密内容。原始私钥字节不会发送到服务器。公钥和证明记录会被存储用于验证和身份显示。

同一种浏览器内 tlock 解密同样适用于 qub 嵌入:当一条已封印的 qub 在第三方页面上通过 <qub-embed> 渲染时,解密仍发生在查看者浏览器中的嵌入 iframe 内。嵌入并未改变信任模型—— 明文永不会在 qub 服务器上被解密。

3.6 公开署名—— 可选

封印的 qub 不会携带指向其创作者的链上指针,除非创作者明示选择附加一个。当你封印一条 qub 时,参考创作者应用仅在日期选择步骤上启用"公开署名"时才会发出 Author 存储标签(你签名公钥的 64 字符十六进制指纹)。当开关关闭——默认状态——不会写入 Author 标签,且该 qub 在永久存储中未署名:存储中没有任何东西把这次上传与你的 handle、你的邮箱或你的其他 qub 关联起来。当开关打开时,指纹在查看者渲染时通过 §6.3 / §10 中的证明链解析为你的 @handle,倒计时在开启前会显示"由 @{handle} 封印"。

这是有意防范若 Author 标签始终开启会带来的枚举风险的一项保护:得到一位创作者指纹的第三方,否则可以按该标签在永久存储中做 grep,重建该创作者完整的历史输出。可选署名关闭了这条通道——只有创作者明示选择署名的那些 qub 才会以指纹形式出现在永久存储中。

/u/{handle} 个人页面是一张已验证身份卡—— handle、可选的显示名 + URL、"已验证邮箱"徽章(不显示地址),以及密码学指纹的简短形式。它不会列出某位创作者的 qub。希望查看某位创作者特定 qub 的访客,可直接通过该 qub 的传递链接前往。

3.7 外层加密包装

即使时间锁解密在数学上是可能的——一旦用于绑定轮次的 drand 签名被发布——单独的标准时间锁层也足以让索引器批量解密可发现的 qub。私有传递通过在时间锁加密的字节周围添加额外的对称层来关闭该通道(协议 §13)。公共传递故意省略该包装层,以便通知、嵌入和发现链接可以在没有秘密片段的情况下工作。

包装使用 AES-256-GCM,一种由 NIST 标准化的经认证密码,每条 qub 由你浏览器的 CSPRNG 生成一把新鲜的 256 位密钥 K。K 与该 qub 的 qub_id 绑定为附加认证数据,因此来自一条 qub 的密钥无法被复用以解密另一条 qub。

在默认的私密浏览器流程中,K 永远不会到达我们的服务器。 它被编码到共享链接的 URL 片段中(https://qub.social/c/<tx_id>#<base64url(K)>浏览器不会将 URL 片段传输到服务器——RFC 3986 将片段置于请求之外——因此 qub.social、存储网关、CDN 和请求监控在该流程中无法看到 K。存储的 OuterWrapper 是可识别的结构化 CBOR,但其认证密文字段隐藏了内部内容 SealedQub 结构,不能在没有 K 的情况下打开。

净结果:

Worker 的服务端 /api/v1/seal 端点(供 AI 智能体与其他 API 调用者使用)要求调用方使用 CSPRNG 生成 K、在本地保留它,并将其作为 wrapper_key_b64url 提供。在这条被明示信任的路径上,Worker 必然会在内存中同时看到明文与 K,但两者都不持久化。一个强制的 Idempotency-Key 可防止丢失的响应再创建出第二条计费 qub,而调用方保留的 K 则可与被重放的、不含片段的 URL 相结合。这与默认的浏览器路径不同:在浏览器路径上,除非创作者明示启用恢复,否则 K 绝不到达 Worker。


4. 传输与边缘

4.1 TLS

到 qub 的浏览器流量通过 Cloudflare 边缘的 HTTPS 提供服务。响应设置了 HTTP 严格传输安全(max-age=63072000; includeSubDomains; preload)。确切的协商 TLS 版本和加密套件由活动边缘配置决定,而不是由应用程序代码声明的。我们不公开单独可访问的源服务器。

4.2 内容安全

编译后的客户端以严格的 content-type 与缓存头进行服务。SPA 外壳是单一源。我们不为分析或广告嵌入第三方脚本。本产品中的两个第三方接触点都被严格限定:购买流程通过完整页面跳转完全离开 SPA 进入 Stripe 托管结账(https://checkout.stripe.com/…)—— Stripe 的 UI 永不在我们的源中执行,我们也永不看到卡数据;封印流程加载 Cloudflare 的 Turnstile 小部件,一种保护隐私的 CAPTCHA 替代方案,由 Cloudflare 在其自身沙箱化的 iframe 中渲染。两者都无法读取页面的其余部分。

这 qub 嵌入 iframe(从提供) qub.social/embed/{tx_id} 并由第三方网站加载 embed.js) 拥有自己的内容安全策略。它的 connect-src 允许列表是 'self', https://qub.social, https://arweave.net, https://ar-io.dev, https://permagate.io, https://api.drand.sh,和 https://drand.cloudflare.com. 该iframe运行于 sandbox="allow-scripts allow-top-navigation-by-user-activation" (不是 allow-same-origin): 主机页面无法读取其 DOM,并且除非用户操作,否则无法导航主机。

4.3 CORS 与抓取范围

浏览器客户端仅向以下目标发起抓取请求:

嵌入的目标位置由其 CSP 强制执行。主 SPA 的预期目标位置在代码和配置中固定,并通过浏览器和集成检查进行验证;子资源完整性(Subresource Integrity)不是网络目标控制。

该嵌入通过允许的 qub/storage 源获取存储的字节,使用 URL 片段中的 K 在浏览器中解封私有有效载荷,并从两个允许的 drand 源获取揭示时间轮次签名。主 SPA 使用四端点回退集。 config/drand-endpoints.json (drand.cloudflare.com, api.drand.sh, api2.drand.sh,和 api3.drand.sh) 因此,一个端点的中断不会阻止揭露。嵌入的 CSP 拒绝其明确列表之外的连接。


5. 服务端基础设施

5.1 无服务器边缘

我们的 API 完全运行在边缘的托管无服务器运行时上。没有虚拟机、没有容器,也没有由我们管理的常驻服务器进程。这极大地减小了我们需要负责的攻击面:我们不运行操作系统、不运行 Web 服务器,也不运行需要我们打补丁的应用运行时。

一个单独的公共 CORS 中间件适用 Access-Control-Allow-Origin: * 到以下已实现的路径集合: /embed.js, /embed/v1.js,下面的一切 /embed/; /api/v1/telemetry; /api/v1/openapi.json; 所有下方 /api/v1/qub/ (包括字节、元数据、证明、参与、通知和推送子路由);所有内容都在 /api/v1/log/; 公共句柄查找下 /api/v1/handle/;而公共头像在下方读取 /api/v1/identity/avatar/. 它的飞行前许可 GET, POST,和 OPTIONS 和 Content-Type 请求头。这个基于前缀的表面比嵌入当前的调用范围更广,因此在这些前缀下的每个处理程序都必须继续执行自己的验证、认证、速率限制和滥用控制。其他 API 路径保留 qub.social 限制的 CORS 策略。

5.2 存储

默认浏览器消息流程不会在 qub 基础设施上保留明文。Builder /api/v1/seal 在内存中处理明文和 K,但不持久化任何一个。Pact 阶段必须存储已签名的结构化 pact,直到它被联合签署、撤回或过期。选择加入的恢复存储一个传递能力(完整的携带片段的链接),以便以后可以恢复。因此,我们不将整个存储层描述为“仅元数据”。

5.3 密钥

秘密(签名钱包、提供者令牌和 HMAC 密钥)通过平台秘密/环境绑定提供,而不是通过源代码管理。运行时组件只接收它们所需的绑定。轮换和重叠程序是组件特定的;我们不声称有单一的通用自动或审计轮换机制。

5.4 日志与遥测

每个 API 请求都写入结构化 JSON 日志,关联 ID 在 X-Request-Id 响应头中暴露。客户端遥测是匿名的——没有设备标识符、没有 IP 地址、没有内容预览。事件在内存中缓冲、按最大努力批量刷出;刷出失败即丢弃,不重试。遥测被设计为可以在网络层禁用而不影响产品。


6. 身份认证

6.1 Magic-Link 登录

登录使用通过 HMAC 签名的单次使用令牌,该令牌发送到您的电子邮箱收件箱。该链接有效期为 15 分钟,并且兑换是原子性申领的,因此同时或重复使用将失败。在成功后,浏览器会接收到一个不透明的 __Host-qub_session 带有……的饼干 Secure, HttpOnly, SameSite=Strict,和 Path=/ 属性。

会话有30天的空闲限制和90天的绝对限制,每24小时轮换一次,并且仅接受前一代以提供120秒的丢失响应宽限期。敏感账户变更要求在前10分钟内进行身份验证。HMAC签名密钥是平台绑定的;仅读取元数据本身不会生成有效令牌。

6.2 API 密钥(开发者层)

开发者 API 密钥使用前缀 qub_sk_,便于辨识与 grep。每把密钥:

管理员密钥管理端点由独立的管理员凭据保护。

6.3 邮箱证明(作者署名签名)

将邮箱地址与签名密钥绑定要求:

  1. 持有私签名密钥(你签一段挑战)
  2. 持有该邮箱(你输入通过邮件投递的 6 位数验证码)

任一项单独均不足够。撤销是写在你自己账号上的已签名记录,立即生效;查看者在抓取证明时会看到被撤销的状态并相应显示。


7. 支付

卡片输入和处理在 Stripe 托管的结账页面内进行。我们从不接收卡号、有效期或 CVC。我们确实会在权利/API 密钥记录中存储 Stripe 客户和订阅标识符、订阅状态以及周期数据,以便访问、续订、计量、取消和退款可以进行对账。Stripe 的隐私和安全声明管理其对支付数据的处理。

封印端点将权益记录与设备标识符交叉校验,对已登录用户还会与所链接的身份交叉校验。没有用户通过 magic-link 登录明示恢复,权益就无法在多设备间被复用。


8. 抗滥用

8.1 机器人检测

封印流程由一项保护隐私的 CAPTCHA 替代方案把关,它不为追踪使用 cookie,也不为广告进行指纹识别。挑战失败的请求会在任何与封印相关的处理发生之前被我们的边缘 Worker 拒绝。

8.2 限流

限流在多个层面执行:

计数器和原子声明根据端点的一致性要求分布在 KV、持久对象和平台速率限制绑定中。受速率限制的请求会返回 429;能够计算重试窗口的端点包括 Retry-After.

8.3 内容审核

默认的浏览器上传路径无法扫描主体:它只接收客户端封装的产物。构建器 /api/v1/seal 路由会短暂地看到明文,而协议分阶段会在最终确定之前保留结构化条款,但这些信任例外并不会将一般的字节盲上传路径变成内容扫描器。操作性管理是一个 拒绝列表 在查看器层:被拒绝列表的 qub 会被我们的查看器拒绝,无论存储的有效负载是否仍然可访问。拒绝列表不会撤回已经发布的持久字节、透明日志条目或永久网络数据。

滥用举报使用举报人 IP 的单向哈希进行限流;为此目的我们不以明文存储 IP。


9. 供应链与构建完整性

9.1 工具链固定

编译器和运行时版本在仓库配置中已固定,依赖关系通过提交的锁文件来解析。CI 检查生成文件的新鲜度和重现性敏感的不变性。我们并不声称每次干净构建在所有支持的机器上都是逐位相同的。

9.2 Lint 与静态分析

工作区在 deny 级别开启了我们最严格的 lint 组。CI 把每一条警告——包括文档链接警告——视为构建失败。这是刻意的:我们将严格的 lint 作为对细微回归的绊线。

9.3 CI 关卡

CI 工作流涵盖了格式化和严格的 lint 检查;Rust、WASM/浏览器、Worker、嵌入和 API 测试;类型检查;代码覆盖率;变异/不变性检查;依赖和工作流静态分析;i18n 键、覆盖率、漂移和恶意代码点检查;生成的文档/API/知识库的新鲜度;文档清单和内部链接检查;样式表和包大小预算;以及 OpenAPI 验证。一些开销较大的变异任务是安排好的,而不是在每次推送时运行。

单个必需 ci 如果任何必需的作业失败,roll-up 将保持为红色。受保护的分支和部署工作流会使用该结果,而不是重复一个较小的安全门。

9.4 变异测试

每周作业对安全关键的纯模块进行变异测试:哈希、规范化 CBOR、封印、开启、线缆格式的 newtype、协议类型校验器以及 handle 命名空间。变异测试回答"我们的测试套件是否能捕捉到细微错误的代码?"—— 如果被变异的实现仍然通过所有测试,我们就知道存在测试覆盖缺口并予以解决。

9.5 Git 钩子

本地钩子(pre-commit、pre-push)镜像 CI 关卡,从而回归在离开开发者机器之前就被捕获。钩子通过仓库脚本安装;它们不在我们的工作流中被绕过,并且如果它们被跳过,CI 是权威关卡。


10. 测试

安全关键代码携带三类测试:


11. 分支与发布卫生

功能分支前进 staging 仅通过 Gate 1 拉取请求:必需 ci 绿色,没有未解决的更改请求,没有合并冲突,并且审查过的分支是干净的;合并是压缩并删除。 main 只能通过大门2前进 staging → main 拉取请求并通过合并提交保留祖先记录。直接推送分支不是发布工作流。

暂存和生产部署是从相应的受保护触发的 staging 和 main CI 后的分支状态。拉取请求代码和分叉凭证不会获取部署密钥。

部署工作流中使用的密钥由我们的 CI 平台限定到部署环境。它们对来自 fork 的拉取请求工作流不可用。


12. 协调披露

如果你认为在 qub 中发现了安全漏洞,我们希望尽快听到——并承诺以专业方式处理这份报告。

我们会在三个工作日内确认收到,并在调查时让你知情。如得到你的同意,我们会在发布说明中署名致谢。

12.1 安全港

如果你的研究遵循上述规则(善意调查、不损害其他用户或服务、合理的披露窗口期),我们不会对你追究法律责任,也不会请求执法机构追究。我们视你的工作为获得授权的测试,并且更希望由你发现 bug,而不是别人。

本安全港适用于:

它不适用于对 qub 团队成员的社交工程、拒绝服务测试,或在为证明问题所必需之外访问其他用户数据。如果你不确定某事是否落入安全港范围,先用同样的 [SECURITY] 主题前缀来询问。


13. 诚实的局限

安全是一种实践,不是一种状态。有些局限值得直接命名:


14. 本页的变更

实质性变更通过更新页面顶部的生效日期来标记。如果某项变更反映的是一项具体的安全改进,我们会在公开变更日志中简要描述。如果某项变更反映的是政策澄清,我们会描述变更了什么、为何变更。

对本页任何内容有疑问,请发送邮件至 support@qub.social,主题前缀 [SECURITY]。


15. 变更日志

版本 生效日期 摘要
1.1 2026年9月23日 将已实施系统与加密声明、传输模式、存储、云服务提供商、会话、API 密钥、支付、持续集成和发布工作流程进行了对齐。
1.0 2026年5月2日 初次出版。