anoni-net / anoni-net/docs

feat: passkey 作為全站基礎功能,明網使用、本機加密儲存、離線可解

Đang mở
#425 2 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
JavaScript
Star
47
Fork
7
Merge trung bình
2 giờ 42 phút
Pull request đã merge (30 ngày)
291

Mô tả

## 目標

讓讀者在明網的 anoni.net 用 passkey 保護存在瀏覽器裡的個人資料。資料留在讀者的裝置上,站方讀不到,斷網時照樣解得開。passkey 存在讀者自己的密碼管理員或裝置的平台驗證器裡,站上維持沒有帳號、沒有登入、沒有伺服器端驗證。

## 機制

WebAuthn 的 PRF 擴充讓 passkey 內部多藏一把秘密,永遠不離開驗證器。網頁每次驗證附一段輸入,讀者通過指紋或 PIN 之後,驗證器回傳固定 32 位元組的輸出。同一把 passkey 配同一段輸入,每次得到同一段輸出。

typage 已經把它做成 age 的收件人類型,程式在 #424 vendor 進來的 `utils/vendor/age/age-encryption/dist/webauthn.js`,不需要新的第三方程式碼:

1. 加密時產生 16 位元組的隨機 nonce,把標籤 `age-encryption.org/fido2prf` 與 nonce 組成兩段輸入交給 passkey 算 PRF。
2. 兩段輸出接起來經 HKDF 得到包裝金鑰,用 ChaCha20-Poly1305 包住 age 的 file key,nonce 明碼放在 stanza 參數裡。
3. 解密時用 stanza 裡的 nonce 重算一次,得到同一把包裝金鑰。

typage 刻意把一次驗證能算的兩段輸出都用掉才湊成一把金鑰,所以一次指紋只能解開一個檔案。

## 硬限制

這幾條要原樣寫進頁面,讀者看完再決定要不要用:

- passkey 綁在 `anoni.net` 這個 origin。鏡像站、onion、localhost 的 origin 不同,用不了。
- Tor Browser 整個關閉 WebAuthn。`tor-browser-153.2.0esr-16.0-1` 分支的 `001-base-profile.js` 仍把 `security.webauth.webauthn` 設為 false,追蹤的 issue 26614 從 2018 開到現在,2026 年 1 月還有更新。
- passkey 遺失,資料永久打不開,沒有密語可以退回。
- 瀏覽器的儲存空間會在空間壓力下被回收,讀者按「清除網站資料」也會一起清。passkey 還在,資料沒了。
- 用手機當電腦的驗證器要掃 QR,需要網路與藍牙,離線時只有本機已解鎖的密碼管理員、平台驗證器與 USB 安全金鑰能用。
- Linux 桌機沒有平台驗證器,要靠密碼管理員擴充或 USB 安全金鑰。

離線本身沒有問題。文件站裝成離線 App 後,從 service worker 快取載入的頁面 origin 仍是 anoni.net,驗證整個在裝置上完成。網域被封鎖也一樣,快取還在就能用。

## 必要條件

下面四項是功能的一部分,少一項就不上線:

1. 匯出要脫離網域。提供「匯出成密語保護的 .age 檔」,流程是用 passkey 解開再用密語重新加密。匯出的檔案用 age CLI 在任何地方都能開,資料才不會跟 anoni.net 這個網域綁在一起。
2. 建立時強制備援收件人。同一份資料同時加密給 passkey 與一把 X25519 備援金鑰,備援金鑰讓讀者下載或抄寫一次,或改用第二把 passkey。age 規定 scrypt stanza 必須單獨存在,所以備援沒辦法是密語。
3. 請求持久儲存。呼叫 `navigator.storage.persist()`,並把結果顯示給讀者,被拒絕時要說清楚資料隨時可能被回收。
4. 改寫站上的承諾。「什麼都不存」要改成「存在你的裝置上,我們讀不到」,兩者是不同的話,指紋示範頁與 `utils/index.md` 的說法都要跟著對齊。

## 架構

- 一站一把 32 位元組的 vault key,存成 age 檔頭的形式放在 IndexedDB:一個 passkey stanza 加一個 X25519 備援 stanza。
- 解鎖時跳一次指紋取回 vault key,只放記憶體,關分頁就消失,可加閒置逾時。
- 各小工具透過共用模組 `js/vault.js` 存取,紀錄用 vault key 以 ChaCha20-Poly1305 加密,noble 的實作已在 vendor 裡。
- RP ID 一開始就明確指定為 `anoni.net`,文件站將來搬到子網域也不會斷。
- 只在讀者按下按鈕時才碰 `navigator.credentials`,頁面載入不探測,跟指紋示範頁的承諾一致。
- 在本機檔案加密小工具裡,passkey 是第三種收件人,排在公鑰模式之後。

## 支援面

2026 年 3 月的狀況,上線前要在真機重測一輪:

| 環境 | 狀況 |
|---|---|
| macOS 15 以上的 Safari 18、Chrome 132、Firefox 139 | 支援 |
| iOS 18.4 以上 | 支援,18.0 到 18.3 有資料遺失的 bug |
| Android Chrome 配 Google 密碼管理員 | 支援,Firefox Android 不支援 |
| Windows 11 配 2026 年 2 月更新,Chrome/Edge 147、Firefox 148 | 支援,Windows 10 全部不支援 |
| 1Password、Bitwarden、Dashlane | 支援 PRF |
| Proton Pass | 未查到說明 |
| Tor Browser | 不支援 |

## 保護範圍

本機加密擋的是拿走裝置、複製瀏覽器資料夾、在共用電腦上翻資料的人,也讓站方永遠讀不到內容。它擋不了解鎖狀態下同一台機器上的惡意程式,也擋不了文件站本身被換掉 JS。後者跟站上其他小工具的信任前提相同,就是讀者信任 anoni.net 送出的程式碼。

## 不做

- 登入、帳號、伺服器端驗證。
- 用 passkey 取代密語模式。密語模式與公鑰模式維持,passkey 是第三種選擇。
- Tor Browser 與 onion 的支援,等上游開放 WebAuthn 再評估。

## 測試

- vault 檔頭的包與解包:用 Node 內建的 HMAC 模擬 PRF 輸出,驗證 stanza 的形狀與 typage 一致,X25519 備援 stanza 用獨立實作解得開。
- 原始碼掃描:vault key 與 PRF 輸出不進 storage、不進網路、不進分析事件。
- WebAuthn 本身沒辦法在 CI 執行,用 DOM 替身照 `check_qrstream_ui.mjs` 的做法走完建立、解鎖、匯出、清除四條路。
- vendor 檔案清單、import map 與 `offline_assets` 的三邊比對沿用 `test_agecrypt.mjs`。

## 先後順序

1. #421 的公鑰模式。
2. 本機檔案加密小工具加 passkey 收件人,含強制備援與匯出。
3. `js/vault.js` 共用模組與第一個使用它的功能。vault 要裝什麼還沒定,威脅模型清單的答案、筆記、讀者自己的檔案都是候選,決定後再拆子任務。

## 相關

- #421 本機檔案加密採 age 格式
- #424 密語模式與「什麼是 age」介紹頁
- typage README 與 `webauthn.js`:
- PRF 支援面整理:
- Tor Browser 的 WebAuthn 追蹤:

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Start by reading #421 and #424, then inspect utils/vendor/age/age-encryption/dist/webauthn.js, js/vault.js, and the existing offline asset checks. Run test_agecrypt.mjs and follow the check_qrstream_ui.mjs DOM-substitution approach to understand the stated test paths. Done requires all four listed prerequisites, the passkey file-tool flow, shared vault integration, aligned wording, and the listed tests and scans.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript
Lĩnh vực
cryptography, documentation, frontend, security
Loại issue
Tính năng
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
30/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.