qub 安全
生效日期: 2026年9月23日 版本: 1.1 — 實施準確性審查
給研究人員 — 快速參考:
- 寄送報告的地點: support@qub.social 帶有主語前綴
[SECURITY]。- 要包括的內容: 漏洞、重現步驟,以及任何概念驗證。
- 我們的回應: 我們確認在三個工作日內收到,並且目標是在九十天內出貨修正。
- 安全港: 我們不會對遵守§12規則的善意研究採取法律行動(不得存取不屬於你的資料,不得造成服務降級,獲得的資料不得超過用於證明問題所需的範圍,給我們合理的披露時間)。
完整細節見§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 輪次綁定,以及任何作者簽名。保持這些聲明的區分就是本頁所遵循的標準。
我們不會要求你信任我們。我們的設計目標,是把對我們的信任要求降到盡可能小;而在確實需要信任的地方,我們會清楚說明被信任的是什麼、以及為什麼。
三條原則驅動著每一個設計決策:
- 將伺服器可以看到的內容降到最低。 在預設的瀏覽器訊息流程中,純文字和封裝金鑰會保留在您的裝置上。伺服器端的 Builder 封裝、契約共同簽署以及明確啟用的復原具有不同的信任邊界,如下所述。我們持有的元資料僅限於所選功能所需的範圍。
- 使妥協局部化。 任何一個元件(我們的伺服器、電子郵件提供者、drand 節點)的違規都不應該洩露尚未到揭露時間的封存內容。
- 讓協定可供稽核。 封存產物可使用公開密碼學端對端驗證;你不必信任 qub,也能驗證 qub 產物。
2. 威脅模型
2.1 我們防護什麼
- 在揭示時間之前,獲得對我們儲存的伺服器端資料的讀取權限的攻擊者。 在預設的私人瀏覽器流程中,他們取得的是元資料和不透明包裝的位元組,而不是明文或 K。這種保護不適用於明確保留以供恢復的 K、在其 drand 回合之後對公眾/裸露傳送的 K,或臨時提供給建構者的明文
/api/v1/seal以及協定工作流程。 - 攔截你瀏覽器與我們基礎設施之間流量的攻擊者。 TLS 在我們的 CDN 邊緣終止;封裝的有效載荷在傳輸前已經加密。
- 篡改已儲存載荷的攻擊者。 外層包裝驗證(若存在)、標準解碼、主體雜湊,
qub_id重新推導、區塊綁定以及可選簽名使篡改無法通過驗證;查看器拒絕渲染它。 - 試圖將偽造的作者電子郵件綁定到簽名金鑰的攻擊者。 電子郵件驗證需要擁有私人簽署金鑰以及發送到電子郵件收件箱的一次性程式碼。
- 受損的 drand 信標操作員。 drand 網路在多個獨立營運者之間使用閾值 BLS 簽名;少數人無法偽造提前發布的簽名。
2.2 我們無法防護什麼
我們坦誠說明我們的局限。qub 無法防御:
- 在你封存之前妥協你的裝置。 本地鍵盤記錄器、惡意瀏覽器擴充功能或對已解鎖裝置的實體存取,可能在撰寫時捕捉明文。
- drand 閾值的崩潰。 多個獨立組織運行 drand 網路,特別是為了使這變得困難,但這在加密上並非不可能:如果足夠多的營運者串通,他們可能會提前取得時鎖金鑰。
- 有效副本的釋放屬性。 一個公共/裸露的 qub 在其 drand 回合後會變得可解密。一個私有/封裝的 qub 額外需要 K;任何獲得儲存的位元組和 K 的人都可以在回合後解密。永久儲存和錨定日誌記錄僅僅通過從 qub 的產品表面移除它們是不可能被召回的。
- 一個可以破解基本加密技術的全球對手 (AES-GCM、BLS12-381 配對假設、SHA3-256、ML-DSA-65)。如果這些基元失效,整個加密生態系統將面臨更大的問題。
3. 客戶端密碼學
在預設的瀏覽器訊息流程中,內容加密發生在上傳請求之前。兩個明確的路徑不同:Builder /api/v1/seal 故意將明文和呼叫者產生的 K 發送給 Worker 以進行內存封存,並且協議分階段/共同簽署將已簽名的結構化協議發送給服務,以便其最終完成雙邊產物。這兩種例外都不應被誤認為是瀏覽器路徑端到端加密。
3.1 時間鎖加密
qub 使用 tlock —— 一種以未來 drand 信標輪為金鑰的基於身分的加密。加密在你的瀏覽器中使用 drand 網路的公鑰進行;解密金鑰僅在到達目標輪時由 drand 網路公開釋放。包括我們在內的任何人,都無法提前重建解密金鑰。
我們以 quicknet 鏈為目標:
- 3 秒一輪
- 非鏈式模式(每一輪相互獨立)
- BLS12-381 G1 簽名
- 鏈雜湊
52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971
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 的情況下打開。
淨結果:
- qub.social 無法僅從儲存的資料解密預設私人瀏覽器封印。 資料儲存遭到入侵時,會在沒有 K 的情況下接觸到不透明的密文。公開 qubs 和啟用了恢復功能的 qubs 在設計上具有不同的曝露方式。
- 片段丟失在未選擇恢復通道的情況下是無法恢復的。 如果你保存了沒有片段的私人連結並且未啟用恢復功能,通過該連結 qub 將無法讀取。出於這個原因,密封流程會顯示明確的「保存此 URL」提示。
- 選擇性恢復。 當你選擇接受特定 qub 的創作者生命周期電子郵件且電子郵件與你已驗證的身分匹配時,我們會在上傳時接受 K,將完整的傳送 URL 儲存在你身分的封存歷史記錄中,並在封印確認電子郵件中使用它作為連結。這種交易——以伺服器端的恢復渠道換取一些端到端的純度——僅在明確選擇加入且僅限於該 qub 時才會啟動。預設策略是加密銷毀。
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 與抓取範圍
瀏覽器客戶端僅向以下目標發起抓取請求:
- 我們自己的 API (
api.qub.social以及分期等價物 - 儲存閘道(唯讀,用於封裝私有或裸公共位元組檢索 — §3.6)
- drand 信標端點(唯讀,用於揭示時間回合簽名)
嵌入的目標由其 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 儲存
- 元資料和協調儲存 持有身分和認證記錄、權限和帳單參考、API 金鑰記錄、拒絕名單條目、會話、冪等狀態、佇列,以及速率限制 / 並發狀態。不同的一致性需求使用 KV、D1 和 Durable Objects,而不是一個通用的儲存。
- 我們的物件儲存 也是一種耐用的基底。它保存了由上傳、透明度日誌葉子和坐標鍵控的 Merkle 節點、錨點材料、結構化事件日誌以及回應/元資料快取所確認的精確封裝或裸 qub 位元組。
- 永久公共儲存 保存透明度日誌錨點,並且對於 T3 路徑或延遲發佈,保存個別 qub 交易。我們並不營運該網路。除非持有者也擁有 K,否則私有瀏覽器有效負載在那裡仍然是不透明的;公開/裸露的有效負載故意沒有那個額外的連結能力層。
預設瀏覽器的訊息流程不會在 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。每把金鑰:
- 綁定到一個帳戶、範圍,以及可選的 IP CIDR 允許清單
- 以原始形式顯示一次;持久記錄保留其 SHA-256 雜湊,而不是持有者的秘密
- 可以進行旋轉,帶有一小時的寬限映射,其中舊金鑰解析到替換金鑰
- 具有獨立的配額和速率限制狀態
- 從不完整記錄登入;日誌僅記錄關鍵識別碼
管理員金鑰管理端點由獨立的管理員憑據保護。
6.3 信箱證明(作者署名簽名)
將信箱地址與簽名金鑰綁定要求:
- 持有私簽名金鑰(你簽一段挑戰)
- 持有該信箱(你輸入透過郵件投遞的 6 位數驗證碼)
任一項單獨均不足夠。撤銷是寫在你自己帳號上的已簽名記錄,立即生效;查看者在抓取證明時會看到被撤銷的狀態並相應顯示。
7. 支付
信用卡輸入和處理在 Stripe 托管的結帳頁面內進行。我們從未收到信用卡號、有效日期或 CVC。我們會將 Stripe 的客戶和訂閱識別碼、訂閱狀態以及週期資料儲存在權限/API 金鑰記錄中,以便能夠對存取、續訂、計量、取消和退款進行對帳。Stripe 的隱私與安全聲明管理其對支付資料的處理。
封印端點將權益記錄與裝置標識符交叉校驗,對已登入使用者還會與所連結的身分交叉校驗。沒有使用者透過 magic-link 登入明示恢復,權益就無法在多裝置間被複用。
8. 抗濫用
8.1 機器人檢測
封印流程由一項保護隱私的 CAPTCHA 替代方案把關,它不為追蹤使用 cookie,也不為廣告進行指紋識別。挑戰失敗的請求會在任何與封印相關的處理發生之前被我們的邊緣 Worker 拒絕。
8.2 限流
限流在多個層面執行:
- 對封印、讀取與認證端點的按 IP 與按金鑰限制
- 對 magic-link 請求的按信箱限制(防止信箱泛洪)
- 對約定邀請郵件的按對手方限制(每個收件人地址每個 UTC 日 10 封,是首要的垃圾郵件轉發緩解;正當約定幾乎從不接近該上限)
- 對遙測提交的按 IP 限制
計數器和原子聲明根據端點的一致性需求分佈在 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. 測試
安全關鍵程式碼攜帶三類測試:
- 單元測試在已知輸入上驗證預期行為,包括從協議規範導出的測試向量。
- 屬性測試產生數千個任意輸入並斷言不變式:規範化 CBOR 往返、簽名驗證往返、信箱綁定謂詞、約定確認確定性。
- 跨實現測試驗證我們的客戶端與服務端實現在規範化編碼上位元組對位元組一致。這在兩套實現之間出現分歧到達生產之前就將其捕獲。
11. 分支與發佈衛生
功能分支前進 staging 僅通過 Gate 1 拉取請求:必需 ci 綠色,沒有未解決的變更請求,沒有合併衝突,並且已清理審查過的樹;合併方式為壓縮並刪除。 main 僅通過第二門前進 staging → main 拉取請求並透過合併提交保留祖譜。直接分支推送不是發布工作流程。
階段和生產部署是從相應的受保護觸發的 staging 和 main CI 後的分支狀態。拉取請求的程式碼和分叉憑證不會接收部署機密。
部署工作流中使用的金鑰由我們的 CI 平台限定到部署環境。它們對來自 fork 的拉取請求工作流不可用。
12. 協調披露
如果你認為在 qub 中發現了安全漏洞,我們希望盡快聽到——並承諾以專業方式處理這份報告。
- 發送郵件至
support@qub.social,主題前綴[SECURITY]。 - 描述漏洞、複現步驟以及任何概念驗證。
- 在公開披露之前給我們一個合理的窗口期(通常為 90 天)。
- 不要存取不屬於你的資料,不要降低其他使用者的服務,不要保留在研究中所獲資料多於證明問題所必需的部分。
我們會在三個工作日內確認收到,並在調查時讓你知情。如得到你的同意,我們會在發佈說明中署名致謝。
12.1 安全港
如果你的研究遵循上述規則(善意調查、不損害其他使用者或服務、合理的披露窗口期),我們不會對你追究法律責任,也不會請求執法機構追究。我們視你的工作為獲得授權的測試,並且更希望由你發現 bug,而不是別人。
本安全港適用於:
- 在 qub.social 線上正式服務上的研究(不包括我們為此公佈的測試夾具)。
- 對我們公開發佈的二進位檔案與開源的 qub-core / qub-app crate 的逆向工程。
- 影響 qub 的任何漏洞類別——協議、應用、基礎設施、供應鏈。
它不適用於對 qub 团隊成員的社交工程、拒絕服務測試,或在為證明問題所必需之外存取其他使用者資料。如果你不確定某事是否落入安全港範圍,先用同樣的 [SECURITY] 主題前綴來詢問。
13. 誠實的局限
安全是一種實踐,不是一種狀態。有些局限值得直接命名:
- 我們是一個小团隊。我們的審查深度無法匹配大公司專門的應用安全職能。我們以嚴格的自動化關卡與最小化的攻擊面來補償,但我們不宣稱無懈可擊。
- 我們儲存後端的永久性是一扇單向門。如果一個錯誤導致封印內容比預期更早可解密,我們無法撤銷。我們以相稱的謹慎對待封印流程。
- drand 網路是一項外部依賴。drand 的災難性故障會影響每一條 qub 的開啟行為。我們監控 drand 健康狀況,並在必要時為鏈遷移準備了應急文件。對於開啟日期超過 2 年之後的 qub,封印時確認彈窗會展示明確披露:長期 qub 依賴於 drand 鏈的耐久性,未來的 drand 鏈遷移可能需要恢復步驟才能開啟該 qub。對於開啟日期超過 5 年之後的 qub,你必須額外勾選一個確認框,確認你已經閱讀並接受該風險,封印才會繼續。
- 我們依賴的密碼學原語是經過標準化與廣泛審查的,但密碼學在演進。在有選擇的地方(後量子簽名、經認證加密),我們會選擇更保守的那一種。
14. 本頁的變更
實質性變更透過更新頁面頂部的生效日期來標記。如果某項變更反映的是一項具體的安全改進,我們會在公開變更日誌中簡要描述。如果某項變更反映的是政策澄清,我們會描述變更了什麼、為何變更。
對本頁任何內容有疑問,請發送郵件至 support@qub.social,主題前綴 [SECURITY]。
15. 變更日誌
| 版本 | 生效日期 | 摘要 |
|---|---|---|
| 1.1 | 2026年9月23日 | 將已實現系統與加密聲明、傳輸模式、儲存、CSP、會話、API 金鑰、支付、CI 和發布工作流程進行了對齊。 |
| 1.0 | 2026年5月2日 | 初版刊行。 |