パスワード管理
PotatoでのPBKDF2-SHA256によるパスワードハッシュ化、管理者用CLIとAPIによるリセット、トークンを使ったセルフサービスのリセットリンク、SQLiteまたはPostgreSQLでの認証情報の保存を設定します。
v2.4.0の新機能
Potatoの認証はPBKDF2-SHA256を100,000回反復し、ユーザーごとに個別のソルトを使います。これはNISTがパスワードの安全な保存方法として推奨しているものと同じ方式です。このページでは、パスワードがどう保存されるか、リセットの方法、そしてサーバーを再起動しても認証情報を保持する方法を扱います。
対象はin_memoryとdatabaseの2つの方式です。OAuthではパスワードをIDプロバイダーが管理するため、混在モードのローカルアカウントを除いて、ここでの内容は当てはまりません。
セキュリティの実装
パスワードはsalt$hashという形式で保存されます。
- 32文字の16進ソルト(ユーザーごとに固有)
- 64文字の16進ダイジェスト。パスワードからそのソルトと100,000回の反復でPBKDF2-HMAC-SHA256により導出したもの
- タイミング攻撃を防ぐための
hmac.compare_digestによる定数時間比較
ソルト付きパスワードにSHA-256を1回かけるだけでは、大規模な総当たり攻撃が可能なほど高速です。推測1回ごとのコストを高くしているのは、100,000回の反復です。
user_config.jsonファイルに平文のパスワードが残っている場合、Potatoが読み込む際に固有のソルトで自動的に再ハッシュされます。手動での移行作業は不要です。
デフォルトの設定
デフォルトでは、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複数のサーバーインスタンスで1つのユーザー名簿を共有する場合や、名簿をアノテーションの隣以外の場所に置きたい場合は、パスを指定してください。新規登録とパスワード変更は発生した時点で書き込まれます。
ユーザー名簿はアノテーション出力と一緒に保管してください。 アノテーターのディレクトリはユーザー名にちなんで命名されます。出力ディレクトリが残ったまま名簿だけが失われると、次にそのユーザー名を入力した人が他人の作業を引き継いでしまいます。そのため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時間有効な1回限りのトークンを生成します。リセット用リンクは管理者が伝えられるよう画面に表示され、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を渡します。
パスワードなしモード
授業でのデモ、短時間の調査、外部認証(MTurk、Prolific)を使うタスクでは、パスワードを完全に無効にできます。
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とリバースプロキシの設定
- 管理ダッシュボード:アノテーターアカウントの管理
実装の詳細については、ソースドキュメントを参照してください。