認証とセキュリティ
Libre WebUI は、JWT セッションを使用するローカルユーザーアカウントに対応しています。新規インストールでは、 ローカル管理者を 1 人だけ必ず初期登録できます。それ以降のローカルアカウントまたは OAuth アカウントについて、 一般公開の登録は既定で無効です。
初回セットアップ
データベースにユーザーがいない場合:
- Libre WebUI に初回セットアップ画面が表示されます。
- ユーザーが最初のローカルアカウントを作成します。
- そのアカウントに
adminロールが割り当てられます。 - 明示的に有効化しない限り、それ以降の一般公開の登録はすべて無効のままです。
既存のデータベースでは、現在のユーザーとロールが維持されます。
ローカルアカウント
ローカル登録には次の情報が必要です。
- ユーザー名
- 12 文字以上、UTF-8 で 72 バイト以下のパスワード。大文字、小文字、数字を含める必要があります
- 任意のメールアドレス
パスワードは保存前に bcrypt でハッシュ化されます。ログインと登録のルートにはレート制限があります。
登録の承認
一般公開の登録だけではアクセス権は得られません。公開登録フォームまたは OAuth プロバイダーから作成された
すべてのアカウントは pending 状態で開始し、サインインする前に管理者の承認が必要です。
唯一の例外は初期登録です。空のデータベースで最初に作成される実際のアカウントは、アトミックに
admin ロール、active 状態で作成されるため、新規インストールでも利用可能な管理者が確保されます。
それ以降の登録はすべて審査を待ちます。
承認待ちのユーザーに表示される内容:
- 登録は成功しますが、セッショントークンは返されません。API は
202とapprovalRequired: trueを返し、 UI には管理者によるアカウント承認が必要だと表示されます。 - 正しい認証情報を使ったパスワードサインインも
403で拒否され、コードACCOUNT_PENDING(「アカウントは管理者の承認を待っています」)が返されます。OAuth サインインの場合は、?approval=pendingを付けてログインページへリダイレクトされます。 - 認証済みリクエストのたびにアカウント状態がデータベースから再読み込みされるため、
アカウントが
activeである期間を超えてセッションが存続することはありません。
管理者に表示される内容:
- ユーザー管理には、待機中のアカウントを一覧表示する 承認待ち カードがあり、アカウントごとに アカウントを有効化 と拒否の操作があります。拒否は削除を意味し、別の停止状態はありません。
- サインイン中の管理者にはアプリ内で通知されます。Users 項目にバッジが付き、新しい登録が届くと
トーストが表示されます。承認待ちの概要は約 1 分ごとに取得されます
(
GET /api/users/pending-approvals、管理者のみ)。 - 承認(
PATCH /api/users/:id/approve、管理者のみ)では、承認した管理者と日時が記録されます。 ロールは変更されません。承認されたアカウントは、管理者が昇格させるまでuserロールのままです。 承認はユーザーが次にサインインを試みた時点で有効になり、何かを再作成する必要はありません。
アップグレードしても既存アカウントには影響しません。この機能の提供開始後に一般公開の登録から 作成されたアカウントだけが、承認待ちとして開始します。管理者がユーザー管理から作成したアカウントは、 直ちに有効になります。
一般公開の登録を意図的に有効にする
登録は既定で無効です。新しいローカルアカウントまたは OAuth アカウントを受け付ける期間に限り、 次のバックエンド環境変数を設定してください。
ENABLE_SIGNUP=true
予定した登録期間が終了したら、false に戻してください。
一般公開の登録を停止しても、既存のローカルユーザーと OAuth ユーザーは引き続きサインインでき、
管理者はユーザー管理から引き続きアカウントを作成できます。
ENABLE_SIGNUP=false でも、空のデータベースではローカル管理者を 1 人だけ必ず登録できます。
OAuth がその初期登録枠を取得することはできません。非公開のリモート環境では、アプリケーションを起動する前に、
ホスト名を Cloudflare Access などの ID 許可リストで保護し、その保護されたルートから最初の管理者を作成してください。
ロール
| ロール | 用途 |
|---|---|
admin | インスタンス管理、ユーザー管理、システム設定、信頼された Work ランタイムの運用 |
user | 通常のチャット、モデル、Persona、ドキュメント、設定のワークフロー |
モデルのインストール、削除、コピー、push、unload はホストリソースを変更するため、管理者のみに制限されています。
Work のアクセス権
Work では選択したモデルが管理対象コンテナ内で任意のコマンドを実行できるため、既定では管理者のみに制限されています。 管理者は設定の User Management タブから、すべてのアクティブユーザーに Work を開放できます。この設定は再起動後も保持され、 開いているターミナルセッションも含めて直ちに反映されます。ホストフォルダーのワークスペースはサーバーのパスを bind mount するため、どのモードでも管理者専用です。Work の利用を許可されたすべての人を、単なる WebUI ユーザーではなく、 信頼されたランタイム運用者として扱ってください。
管理者権限は、既存の JWT にキャッシュされたロールだけではなく、データベース上の現在のロールに対して確認されます。 そのため、管理者を降格すると Work へのアクセス権が直ちに取り消されます。バックエンドは続いて、タスク記録と 名前付きボリュームを維持しながら、実行中の処理を中止し、そのユーザーの Work コンテナとプレビューの停止を試みます。 Docker のクリーンアップに失敗してもアクセス権は取り消されたままで、ロール変更時にクリーンアップ失敗が報告されます。 運用者は Docker へのアクセスを復旧して、クリーンアップを再試行する必要があります。
ユーザーを削除すると、そのユーザーの Work データも破壊的に削除されます。Libre WebUI は最初に、そのユーザーの 管理対象コンテナを停止して Work ボリュームを削除し、その後にアカウントとデータベースレコードを削除します。 Docker がクリーンアップの成功を証明できない場合、アカウントの削除は失敗します。管理者はランタイムの問題を修正し、 再度削除を試みることができます。
グループとリソースのアクセス付与
管理者は設定の User Management タブからグループを作成し、メンバーを管理できます。グループはリソースへのアクセスを
付与する主体です。チャット、ノート、ドキュメント、ナレッジコレクション、フォルダー、Persona、プロンプト、
スキル、カレンダーの所有者は、アクセス API を通じてユーザーまたはグループに read、write、admin の
アクセス権を付与できます。共有可能なすべての画面で同じ共有ダイアログが使われます
(共有を参照)。管理者も同じ方法で、登録済みツールサーバーをユーザーまたはグループに限定できます。
リソースは既定で非公開のままで、グローバルな admin ロールによって他ユーザーのコンテンツへアクセスできるわけでは
ありません。メンバーシップはリクエスト時に評価されるため、メンバーを削除すると、グループから付与されたアクセス権も
直ちに取り消されます。設定の User Management タブの「有効なアクセス権」表示では、ロール、グループ、機能アクセス、
そのユーザーに届くすべての付与を一覧にして、「このユーザーがアクセスできるのはなぜか」を確認できます。
セキュリティ監査ログ
ログインと失敗、ログアウト、セッションとトークンの失効、ユーザー、グループ、付与、トークンの変更といった
セキュリティ上重要な操作は、使用状況分析とは別の追記専用監査ログに記録されます。保存前に詳細情報は編集されます。
シークレットに見えるキーは削除され、ペイロードのサイズは制限されるため、パスワード、トークン、プロンプト内容が
ログに入ることはありません。グループと付与の変更では、同じデータベーストランザクション内に監査イベントを書き込むため、
記録を残さず変更だけが存在することはありません。管理者は設定の User Management タブからログを照会できます。
保持期間の既定値は 180 日です(AUDIT_RETENTION_DAYS)。
セッション
バックエンドは JWT_SECRET で JWT に署名します。本番環境では固定のシークレットを設定してください。
JWT_SECRET=replace-with-a-long-random-secret
JWT_SECRET を変更すると、既存のセッションは無効になります。ローカルおよび OAuth のログイントークンは
JWT_EXPIRES_IN を使用し、既定値は 7d です。この値を変更すると、新しいセッションに反映されます。
WebSocket 接続では永続トークンを、有効期間が短く 1 回だけ使える ticket と交換し、基になるセッションの
有効期限が切れると接続を閉じます。
ログインするたびに、JWT に紐付いたサーバー側のセッションレコードも作成されます。 設定 → セッションには、各デバイスのサインイン方法、最初と最後のアクティビティ、有効期限が表示されます。 そこでセッションを取り消すと(または「ほかのセッションからサインアウト」を選ぶと)、すべての replica で トークンが直ちに無効になり、稼働中の WebSocket 接続が閉じられます。ログアウトでも同じ方法で現在のセッションが 取り消されます。この機能より前に発行されたトークンには session id がないため、有効期限までは有効です。 ただし、新しいログインから「ほかのセッションからサインアウト」を実行すると、アカウント単位の cutoff も記録され、 それらのトークンは拒否されます。
二要素認証とパスキー
設定 → セッションでは、二要素認証とパスワードなしのサインインを管理できます。
- 認証アプリ(TOTP)。 登録時には base32 のシークレットと、任意の認証アプリで使える
otpauth://リンクが 表示されます。最初の 6 桁コードを確認すると有効になり、1 回限りのリカバリーコードが 10 個表示されます。 以後、パスワードログインではセッションの代わりに有効期間の短い challenge が返され、TOTP コードまたは リカバリーコードを使ってPOST /api/auth/mfa/verifyがサインインを完了します。受け付けた各コードの timestep が 記録されるため、傍受されたコードを再利用することはできません。リカバリーコードは keyed one-way lookup token としてのみ保存され、それぞれ正確に 1 回だけ機能します。無効化またはリカバリーコードの再生成には、要素の再証明が必要です。 - パスキー(WebAuthn)。 「パスキーでサインイン」は discoverable credential を使用して、パスワードなしで
ログインします。登録時とサインイン時には、ユーザー検証(画面ロック、生体認証、PIN)が必要です。
attestation は
noneとして受け付け、ES256 と EdDSA の credential に対応します。credential material は 保存時に暗号化され、ID は keyed lookup token として保持されます。challenge は 1 回限りで、5 分後に期限切れになります。 0 以外の signature counter が増加しなかった場合は、複製を示す信号として拒否します。パスキーには安全な(HTTPS)origin、 または開発環境のlocalhostが必要です。複数の hostname でインスタンスへアクセスする場合は、WEBAUTHN_RP_IDを設定します。
正しいパスワードの入力後に発行される MFA challenge token は、JWT_SECRET から派生した、ただし別個のシークレットで
署名されます。API リクエストの認証には絶対に使用できず、1 つのアカウントと 1 つの目的に紐付き、成功時に消費されます。
管理者は、すべてのアカウントに二要素認証を必須とすることができます(Users → 二要素ポリシーカード、または
MFA_REQUIRED_MODE=required で固定)。要素を設定していないユーザーは、次回のサインイン時にセッションが発行される前に
登録手順へ案内されます。管理者はアカウント復旧のため、ユーザー一覧からユーザーの TOTP 登録をリセットすることもできます。
パスキーはユーザーが設定から管理するため、そのまま残ります。登録、有効化、検証失敗、無効化、ポリシー変更、
パスキーの登録/削除、管理者によるリセットは、すべてセキュリティ監査ログに記録されます。
MFA はパスワードログインに適用されます。OAuth と OIDC のサインインは ID プロバイダー側の二要素認証に依存するため、 再度 challenge を求めることはありません。API トークンには影響しません。API トークンはセッション認証を一切使用しません。
API トークン
設定 → API keys では、プログラムから利用する personal access token(接頭辞 lwk_)を発行できます。
シークレットが表示されるのは 1 回だけで、保存されるのはハッシュのみです。各トークンには明示的なスコープ一覧
(chat、models、documents、notes、personas、media、work、admin)があります。
バックエンドは各ルート群を必要なスコープに対応付けるため、Notes 専用トークンがチャットや管理機能へアクセスすることは
できず、セッション管理へトークンで到達することは一切できません。トークンには任意の有効期限を設定でき、
最終使用時刻を追跡し、いつでも取り消せます。また、すべての replica を通じてトークン単位のレート制限が適用されます。
admin スコープのトークンを発行できるのは管理者だけで、使用時にもアカウントが admin ロールを保持している必要があります。
chat スコープのトークンは、OpenAI-compatible の公開 /v1 APIでも使用するキーです。
Cloudflare Turnstile
両方のキーを設定すると、Turnstile がパスワードログインと登録を保護します。
TURNSTILE_SITE_KEY=...
TURNSTILE_SECRET_KEY=...
TURNSTILE_EXPECTED_HOSTNAME=chat.example.com
フロントエンドは個別の login および signup action を割り当てます。バックエンドは Cloudflare でトークンを
検証し、hostname または action がリクエストと一致しないレスポンスを拒否します。
TURNSTILE_EXPECTED_HOSTNAME を明示的に設定していない場合、BASE_URL から期待される hostname を取得します。
いずれかのキーがない場合、Turnstile は無効です。
GitHub OAuth
次のように設定します。
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_CALLBACK_URL=https://your-domain.example/api/auth/oauth/github/callback
GitHub OAuth フローは、gh_ 接頭辞のユーザー名を持つローカルユーザーを作成し、既定で user ロールを割り当てます。
Hugging Face OAuth
次のように設定します。
HUGGINGFACE_CLIENT_ID=...
HUGGINGFACE_CLIENT_SECRET=...
HUGGINGFACE_CALLBACK_URL=https://your-domain.example/api/auth/oauth/huggingface/callback
Hugging Face OAuth フローは、hf_ 接頭辞のユーザー名を持つローカルユーザーを作成し、既定で user ロールを割り当てます。
両方の OAuth プロバイダーは、暗号学的にランダムな state 値を、有効期間が短い HttpOnly、SameSite cookie に
紐付けて使用します。callback は、state がない場合や一致しない場合に拒否します。callback が成功すると、
JWT は 60 秒間有効な HttpOnly cookie でフロントエンドへ戻り、直ちに交換されて消去されます。
bearer token が callback URL、ブラウザー履歴、referrer header に置かれることはありません。
リダイレクトと CORS
callback の既定値には BASE_URL、ブラウザーアクセスには CORS_ORIGIN を設定します。
BASE_URL=https://your-domain.example
CORS_ORIGIN=https://your-domain.example
ローカル開発では、Vite の開発用 origin を含めます。
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
デモモード
デモモードはフロントエンドのプレビューモードです。無効化されたデモ用の認証情報があらかじめ入力され、 モック API レスポンスを使用します。本番用の認証モードではありません。
セキュリティチェックリスト
- 強力な
JWT_SECRETを設定する。 DATA_DIRを、アクセス制御された永続ストレージに置く。ENCRYPTION_KEYをデータベースと一緒にバックアップする。- 一般公開の登録には Turnstile を設定する。
- 公開環境では HTTPS を使用する。
- プロバイダーの API キーを、必要最小限のスコープに制限する。
- OAuth callback URL を正確に設定する。
- Work のアクセス権(管理者アカウント、または全ユーザーに開放するモード)は、 バックエンドのコンテナランタイムを任せられる人にのみ付与する。