密碼管理
在 Potato 中配置 PBKDF2-SHA256 密碼雜湊、管理員 CLI 與 API 重置、使用者自助的令牌重置連結,以及 SQLite 或 PostgreSQL 憑據儲存。
v2.4.0 新增
Potato 的認證系統使用 PBKDF2-SHA256,迭代 10 萬次,併為每個使用者配一個獨立的 salt,這也是 NIST 推薦的密碼儲存做法。本頁講的是密碼如何儲存、如何重置,以及怎樣讓憑據在伺服器重啟後繼續有效。
這些內容適用於 in_memory 和 database 兩種方法。在 OAuth 下密碼歸身份提供方管,所以除了混合模式裡的本地賬號之外,本頁內容都不適用。
安全實現
密碼以 salt$hash 的格式儲存:
- 32 個字元的十六進位制 salt,每個使用者各不相同
- 64 個字元的十六進位制摘要,由 PBKDF2-HMAC-SHA256 以該 salt 對密碼迭代 100,000 次匯出
- 通過
hmac.compare_digest做恆定時間比較,以防時序攻擊
迭代次數才是關鍵。對加了 salt 的密碼只做一次 SHA-256 運算,快到足以被大規模暴力破解,所以這裡的匯出過程要跑 100,000 次。
user_config.json 檔案裡已有的明文密碼會在 Potato 載入時自動用各自獨立的 salt 重新雜湊,不需要手動遷移。
預設配置
Potato 預設使用記憶體認證。require_password 是頂層鍵,不屬於 authentication 塊:
require_password: true
authentication:
method: in_memory
user_config:
users:
- username: "annotator1"
password: "initial-password" # will be hashed on first load
- username: "annotator2"
password: "initial-password"持久化憑據
通過登錄檔單建立的賬號會寫入一個 JSONL 檔案,並在下次啟動時讀回來。在 in_memory 下,除非你另外指定,這個檔案就是輸出目錄旁邊的 user_config.json。
基於檔案的持久化
authentication:
method: in_memory
user_config_path: /shared/path/to/user_config.jsonl當多個服務實例共用一份使用者名稱單,或者你想把名單放在標註結果之外的地方時,就指定一個路徑。新註冊和密碼變更會隨時寫入。
把使用者名稱單和標註輸出放在一起。 標註者的目錄以使用者名稱命名,所以名單丟了而輸出目錄還在的研究,下一個輸入該使用者名稱的人就會繼承別人的工作。Potato 會拒絕這次註冊,這也意味著在你恢復該檔案之前,那位標註者進不來。
在 v2.8.3 之前,除非顯式設定了 user_config_path,否則 in_memory 什麼都不寫,賬號也就撐不過一次重啟。如果你在更早的版本上跑過研究,那些賬號已經沒了;標註結果還在。
資料庫後端
SQLite(不需要額外依賴):
authentication:
method: database
database_url: "sqlite:///auth/users.db"PostgreSQL(需要 psycopg2-binary):
authentication:
method: database
database_url: "postgresql://user:password@localhost:5432/potato_auth"Potato 會在首次啟動時建立 users 表,並讓 SQLite 以 WAL 模式執行,以便更好地支援併發讀取。database_url 必須以 sqlite:/// 或 postgresql:// 開頭。你也可以在環境中設定 POTATO_DB_CONNECTION,兩者同時存在時 database_url 優先。
注意:method: database 和 user_config_path 互斥,只能選一種持久化方案。兩者都配置時 Potato 會報錯。
重置密碼
管理員 CLI
從命令列重置密碼:
# Prompts for username and password
potato reset-password config.yaml
# Prompts for the password only
potato reset-password config.yaml --username annotator1管理員 API
用管理員 API 金鑰以程式方式重置:
curl -X POST http://localhost:8000/admin/reset_password \
-H "X-API-Key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "annotator1", "new_password": "new-secure-password"}'金鑰按這個順序取:頂層的 admin_api_key 設定、POTATO_ADMIN_API_KEY 環境變數,或者任務目錄下自動生成的 admin_api_key.txt。
使用者自助的令牌重置
忘了密碼的標註者可以自己重置。他們訪問 /forgot-password 並輸入使用者名稱,Potato 會生成一個有效期 24 小時的一次性令牌。重置連結顯示在頁面上,由管理員轉交;Potato 不發郵件。標註者開啟 /reset/<token> 設定新密碼,令牌隨之作廢。
這個流程不需要任何配置。設定了 require_password: true 時,登入頁上會出現一個 “Forgot Password?” 連結。
管理員也可以直接簽發令牌:
curl -X POST http://localhost:8000/admin/create_reset_token \
-H "X-API-Key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "annotator1"}'
# {"reset_link": "http://localhost:8000/reset/abc123...",
# "token": "abc123...", "expires_in_hours": 24}想換一個過期時間,在請求體裡傳 "ttl_hours": 48。
免密模式
課堂演示、快速研究,或者使用外部認證(Prolific、SONA)的任務,可以完全關掉密碼:
require_password: false
authentication:
method: in_memory標註者輸入任意使用者名稱即可登入,不會出現密碼輸入框。涉及敏感資料或需要驗證身份的任務不建議這樣做。
詳見免密登入。
完整參考
# Top-level: require a password at login (default: true)
require_password: true
# Top-level: key for the admin endpoints
admin_api_key: ${POTATO_ADMIN_API_KEY}
authentication:
# in_memory (default) | database | clerk | oauth
method: in_memory
# in_memory only, mutually exclusive with method: database
user_config_path: users.jsonl
# database only; sqlite:/// or postgresql://
# database_url: "sqlite:///auth.db"
user_config:
users:
- username: "researcher"
password: "secure-passphrase"
- username: "annotator1"
password: "initial-pass"| 端點 | 方法 | 認證 | 說明 |
|---|---|---|---|
/forgot-password | GET, POST | 無 | 申請重置令牌 |
/reset/<token> | GET, POST | 無 | 設定新密碼 |
/admin/reset_password | POST | API 金鑰 | 管理員重置密碼 |
/admin/create_reset_token | POST | API 金鑰 | 生成重置令牌 |
延伸閱讀
- SSO 與 OAuth 認證 — 用 Google、GitHub 或機構 SSO 登入
- 免密登入 — 開放任務的純使用者名稱訪問
- 生產環境部署 — HTTPS 與反向代理配置
- 管理後臺 — 管理標註者賬號
實現細節見源文件。