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 以進行內存封存,並且協議分階段/共同簽署將已簽名的結構化協議發送給服務,以便其最終完成雙邊產物。這兩種例外都不應被誤認為是瀏覽器路徑端到端加密。

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 標準化的後量子簽名方案。我們刻意為簽名選擇了一個後量子原語,因為封印內容是永久的:今日能驗證透過的簽名,必須在數十年後——包括大規模量子電腦走向實用之後——仍能驗證透過。

簽名金鑰是在瀏覽器中產生的。本地金鑰會被封裝在不可提取的 WebCrypto 金鑰下,然後再儲存在 IndexedDB 中。如果使用了帳戶範圍的跨裝置恢復功能,一個經 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 標籤始終開啟會帶來的枚舉風險的一項保護:得到一位創作者指紋的第三方,否則可以按該標籤搜索永久儲存,重建該創作者完整的歷史輸出。可選署名關閉了這條通道——只有創作者明示選擇署名的那些 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 嚴格傳輸安全 (HTTP Strict Transport Security)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 強制執行。主要單頁應用程式的預定目標在程式碼和配置中已固定,並由瀏覽器和整合檢查來執行;子資源完整性(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 基礎設施上保存純文字。建構器 /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。我們會將 Stripe 的客戶和訂閱識別碼、訂閱狀態以及週期資料儲存在權限/API 金鑰記錄中,以便能夠對存取、續訂、計量、取消和退款進行對帳。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 工作流程涵蓋了格式化和嚴格的程式碼規範檢查;Rust、WASM/瀏覽器、Worker、嵌入和 API 測試;類型檢查;程式碼覆蓋率;變異/不變性檢查;依賴和工作流程的靜態分析;國際化鍵、覆蓋率、漂移和惡意碼點檢查;產生文檔/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 僅通過第二門前進 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日 將已實現系統與加密聲明、傳輸模式、儲存、CSP、會話、API 金鑰、支付、CI 和發布工作流程進行了對齊。
1.0 2026年5月2日 初版刊行。