Skip to content

パスワード管理

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ブロックの一部ではなく、トップレベルのキーです。

yaml
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です。

ファイルによる永続化

yaml
authentication:
  method: in_memory
  user_config_path: /shared/path/to/user_config.jsonl

複数のサーバーインスタンスで1つのユーザー名簿を共有する場合や、名簿をアノテーションの隣以外の場所に置きたい場合は、パスを指定してください。新規登録とパスワード変更は発生した時点で書き込まれます。

ユーザー名簿はアノテーション出力と一緒に保管してください。 アノテーターのディレクトリはユーザー名にちなんで命名されます。出力ディレクトリが残ったまま名簿だけが失われると、次にそのユーザー名を入力した人が他人の作業を引き継いでしまいます。そのためPotatoは登録を拒否します。つまり、ファイルを復元するまでそのアノテーターは再びログインできません。

v2.8.3より前は、user_config_pathを明示的に設定しない限りin_memoryは何も書き込まなかったため、アカウントは再起動で消えていました。それ以前のビルドで調査を実施した場合、それらのアカウントは失われていますが、アノテーションは失われていません。

データベースバックエンド

SQLite(追加の依存パッケージは不要):

yaml
authentication:
  method: database
  database_url: "sqlite:///auth/users.db"

PostgreSQL(psycopg2-binaryが必要):

yaml
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

コマンドラインからパスワードをリセットします。

bash
# Prompts for username and password
potato reset-password config.yaml
 
# Prompts for the password only
potato reset-password config.yaml --username annotator1

管理者用API

管理者用APIキーを使ってプログラムからリセットします。

bash
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?」リンクが表示されます。

管理者がトークンを直接発行することもできます。

bash
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)を使うタスクでは、パスワードを完全に無効にできます。

yaml
require_password: false
 
authentication:
  method: in_memory

アノテーターは任意のユーザー名を入力するだけでログインでき、パスワードの入力欄は表示されません。機微なデータを扱う場合や、本人確認が必要なタスクには向きません。

詳しくはパスワードなしログインを参照してください。

設定リファレンス

yaml
# 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-passwordGET, POSTなしリセットトークンを要求する
/reset/<token>GET, POSTなし新しいパスワードを設定する
/admin/reset_passwordPOSTAPIキー管理者によるパスワードリセット
/admin/create_reset_tokenPOSTAPIキーリセットトークンを生成する

参考資料

実装の詳細については、ソースドキュメントを参照してください。