本ページの位置づけ
これは「Phase 1 authentik連携 実践マニュアル(Headscale/Samba編)」の続き・強化案です。既存の「ID・パスワードでログイン」に加えて、既存のTOTP(ワンタイムパスワード)を削除せず残したまま、より速いログイン手段として「パスキー(生体認証・PIN)」を追加する内容です。ロードマップPhase 1の完成条件(DoD)にある「MFA(TOTP/Passkey)の強制」に対応します。authentik側の設定・実際の端末への登録・実際のログインまで、すべて実機で確認済みです。ただし構築の途中で、パスキーの仕組み自体に起因する重要な注意点(9章)が見つかったため、必ず目を通してください。
目次
1. はじめに:パスキーってそもそも何?
パスキーとは、パスワードの代わりに、指紋・顔認証・PINコード(Windows Helloなど)でログインできる仕組みです。スマートフォンやパソコンにすでに組み込まれているセキュリティ機能を使って本人確認を行うため、「覚える・入力する」パスワードそのものが存在しません。
| パスワード | パスキー |
|---|---|
| 本人が「知っている情報」で証明 | 本人の「体」や「持っているデバイス」で証明 |
| 使い回し・漏洩のリスクがある | サーバー側に漏れても悪用できない(秘密鍵は端末内にしかない) |
| フィッシングサイトに入力してしまう危険がある | 正規サイト以外では原理的に機能しない(フィッシング耐性) |
2. なぜ自宅サーバーに導入するのか
外出先からHeadscale VPN経由で自宅LANへアクセスする際、authentikへのログインはこれまでユーザー名・パスワード+TOTP(ワンタイムパスワードアプリ)で行ってきました(運用ガイド(ワンタイムパスワード編)参照)。TOTPは確実に機能しますが、毎回アプリを開いて6桁の数字を読み取って入力する手間があります。パスキーを追加することで、指紋やPINだけで完了する、より速いログイン手段を選べるようにするのが狙いです。TOTPは削除せず、選択肢として併用する方針で進めています。
3. 用語解説
| 用語 | 説明 |
|---|---|
| WebAuthn | ブラウザとサーバーが、パスワードなしで本人確認をやり取りするための標準規格。パスキーはこの規格の上に成り立っている |
| FIDO2 | WebAuthnを含む、パスワードレス認証全体の業界標準規格の名称 |
| RP ID(Relying Party ID) | そのパスキーがどのドメインに対して有効かを示す識別子。IPアドレスでは登録・利用できず、ドメイン名でのアクセスが必須(8章で実際にこれが原因の問題に遭遇した) |
| 同期型パスキー(Synced Passkey) | Googleパスワードマネージャーやスマートフォンのクラウドキーチェーン等、同じアカウントにログインしているすべての端末に自動複製されるタイプのパスキー。利便性は高いが9章の注意点がある |
| 端末固定型パスキー(Device-bound Passkey) | 特定の物理デバイス(スマートフォン本体、ハードウェアセキュリティキー等)の中だけに存在し、他端末に複製されないタイプのパスキー |
| レジデントキー(Resident Key) | 認証情報を端末側に保存し、ユーザー名の入力すら省略できるようにする設定。パスキーとして機能させるには「必須」または「推奨」に設定する |
| User Verification(ユーザー検証) | 生体認証やPIN入力など、「本当に持ち主か」の追加確認を要求するかどうかの設定 |
| Authenticator Setupステージ | ユーザーが新しいパスキーを登録するための、authentik側の設定単位 |
| Authenticator Validationステージ | ログイン時に「登録済みの認証器(パスキー等)で確認してください」と要求するauthentikの設定単位 |
4. 全体の流れ
【管理者が事前に設定(1回だけ、完了済み)】
1. パスキー登録用のステージ(webauthn-setup)を作る
2. 専用フローに紐付け、ユーザーが自分でパスキーを登録できる状態にする
3. ログイン時のMFA検証ステージがWebAuthnに対応しているか確認する
【利用者が行う】
4. 自分のアカウント設定画面からパスキーを登録(スマートフォンでQRコード読み取り)
5. 次回ログインからは、パスキー(指紋・顔認証・PIN)を選んでログイン
5. STEP 1:authentik側の設定(管理者作業)
以下は実際にauthentikの管理画面(https://auth.yama.mydns.jp/if/admin/、akadminでログイン)で行った設定です。既存のTOTP設定には一切手を加えていません。事前準備として、HTTPS環境が必須(WebAuthnはhttps://またはlocalhostでしか動作しない)ですが、すでにCaddy経由でhttps://auth.yama.mydns.jpが構築済みのため対応済みでした。
5-1. パスキー登録用ステージ(Setup Stage)を作成する
- 左メニュー
Flows and Stages>Stages> 右上の Create - 種類の一覧から WebAuthn Authenticator Setup Stage を選択
- 主な設定項目:
| 項目 | 設定値 | 理由 |
|---|---|---|
| Name | webauthn-setup | 分かりやすい名前であれば任意 |
| 認証器タイプ名 | パスキー | ユーザーが認証器を登録する際に表示される名前 |
| User verification(ユーザー検証) | 必須(Required) | 端末側の生体認証・PIN入力を必須にする。パスキーは本来これが前提の仕組みのため、初期値のまま必須を維持した |
| Resident key requirement(レジデントキーの要件) | 必須(Required) | 「パスキー」として機能させる(認証情報を端末側に保存し、ユーザー名なしでの認証を可能にする)ために必要な設定。初期値のまま必須を維持した |
| Authenticator attachment(認証器の接続方法) | No preference | スマホ・PC内蔵認証器とセキュリティキーのどれでも許可し、ブラウザ側の判断に委ねるため |
| Hints | Selected Hintsに Hybrid (e.g. QR code, phone) を追加 | PCのブラウザでログインする際、「スマホでQRコードを読み取る」選択肢を優先的に提示させるため(同じスマホを使う運用に合わせた設定) |
| 登録試行の最大数 | 0(無制限) | 必須項目のため空欄不可。0を入力すると無制限になる |
| Prevent duplicate devices | ON(初期値のまま) | 同じ認証器を重複登録できないようにする初期設定をそのまま採用 |
Createで作成する。
5-2. ユーザーが登録できるようフローに組み込む
Flows and Stages>Flows> Create で、専用フローwebauthn-setupを新規作成する(指定:ステージ設定/認証:認証が必須)- 作成したフローの「Stage Bindings」タブを開き、Bind stage > 5-1で作成した
webauthn-setupステージを追加 - さらに、5-1のステージ設定に戻り、「設定フロー」欄にもこのフロー
webauthn-setupを指定する。ステージとフローを相互に紐付けるのがポイント(片方だけだと登録メニューに出てこない)
これで、ユーザー設定画面の「認証情報」タブの「登録」メニューに「パスキー」が選択肢として表示されるようになる。
5-3. ログイン時の検証ステージを確認する
当初の設計では、ログイン用に新しくAuthenticator Validation Stage(Device classesでwebauthnを選択)を作成し、ログインフロー(default-authentication-flow)に組み込む想定だった。しかし実際に確認したところ、ログイン時のMFA検証ステージ(default-authentication-mfa-validation)には、最初からDevice Classesに「WebAuthn認証器」が含まれていた(静的トークン・TOTP・WebAuthn・Duo・SMS・メール、全てチェック済み)。そのため、新しい検証ステージを追加作成する必要はなく、このステージについては変更不要だった。
NOTE:計画時の設計と、実際に必要だった作業の差
当初の設計(事前検討時点)では「Setup Stage」と「Validation Stage」の両方を新規作成する想定だったが、実際にはこの環境のdefault-authentication-mfa-validationがすでにWebAuthnに対応済みだったため、新規作成が必要だったのは5-1・5-2の「登録用」のステージとフローのみだった。環境によっては、ログイン側のDevice Classesを個別に確認・追加する必要がある場合もあるため、まずは既存の検証ステージの設定を確認することを推奨する。
5-4. (未実施)完全パスワードレス化への応用
より進んだ設定として、Passwordステージに「このユーザーがパスキー登録済みならスキップする」というポリシー(条件分岐)を追加すれば、パスキーだけでログインを完結させる完全パスワードレス化も可能。ただし、認証フローの設定ミスは最悪の場合「誰もログインできなくなる」事態につながるため、テスト用ユーザーでの事前確認や、akadminの緊急アクセス手段の確保が前提になる。今回はTOTP・パスワード・パスキーを両方使える状態を維持する方針のため、この応用設定は実施していない。
6. STEP 2:スマートフォンでパスキーを登録する(実機手順)
- authentikにログイン中のブラウザで、ユーザー設定画面(右上のアバター)を開く
- 「認証情報」タブ > 「登録」から「パスキー」を選択
- ブラウザに、パスキー登録用のダイアログが表示される(PCの場合、ここで「スマートフォンでQRコードを読み取る」という選択肢が出るはずだが、下記のつまずきポイントに注意)
- スマートフォンのカメラでQRコードを読み取り、指紋・顔認証等で確認すると、登録が完了する
つまずきポイント:ブラウザによってQRコード(ハイブリッド接続)の選択肢が出ない
Linux機のMicrosoft Edgeでは「スマホでQRコードを読み取る」という選択肢が表示されず、USBセキュリティキーのダイアログしか出なかった。原因はその端末のBluetoothがソフトブロックで無効化されていたことだった(sudo rfkill unblock bluetooth / sudo systemctl start bluetoothで有効化)。Bluetoothを有効化してもEdgeでは変化がなかったが、Google Chromeで同じ手順を試したところQRコード画面が正常に表示された。ブラウザによってWebAuthnのハイブリッド接続(QRコード)対応状況が異なるため、うまく出ない場合はChromeで試すのが確実。
つまずきポイント:RP IDはドメイン名必須。自宅Wi-Fiだと管理画面に届かないことがある
WebAuthnの仕様上、RP IDにはIPアドレスを使えず、auth.yama.mydns.jpのようなドメイン名でのアクセスが必須。ところが、自宅Wi-Fi接続中は自宅ルーターのNATループバック非対応(またはDNSにサブドメインのレコードが無い)ため、このドメインにNo route to hostやタイムアウト、DNS解決失敗(NXDOMAIN)になることがある。パスキーの登録・ログイン操作は、必ずスマートフォンのテザリング等、自宅LAN外のネットワークから行う必要がある。
7. STEP 3:パスキーで実際にログインする(実機手順)
- ログイン画面でユーザー名を入力すると、MFAの選択肢が表示される
- 「パスキー」を選ぶと、ブラウザにQRコードが表示される(または、後述9章の理由でブラウザが記憶している場合はそのまま進む)
- QRコードが表示されたら、できるだけ手早くスマートフォンで読み取り、PIN・指紋・顔認証で確認する
- 成功すると「Authenticated as ...、you can now close this window.」と表示される
つまずきポイント:もたつくと「state has expired」になる
QRコードが表示されてからスマートフォン側の確認が完了するまでの間に、authentikのログインフロー自体に有効期限(タイムアウト)が設定されている。QRコードの表示に手間取ったり、スマートフォン側のPIN入力に時間がかかったりすると、「state has expired」というエラーになり、最初からやり直しになる。対処は「新しいログインURLを再発行し、新しいプライベートウィンドウで開き直して、できるだけ手早く進める」こと。何度も失敗する場合は、authentik側のログインフローの有効期限設定を見直す余地がある。
Ubuntu機(実機)から、テザリング回線・Google Chromeのプライベートウィンドウ経由で、パスキーでのログイン(Reauthenticated as ...と表示)まで実際に確認済み。
8. 【重要】Headscale VPN経由でのログインで別の問題にぶつかった話
パスキーの検証中、authentik自体は正しく動いているのに、Headscale VPN(Tailscale互換のVPNコントロールサーバー)への接続だけがなぜか毎回失敗するという別の問題に遭遇した。パスキー導入そのものとは直接関係ないが、同じ検証の流れで見つかった重要な教訓なので記録しておく。
VPNクライアント側でログアウト→再ログインを繰り返しても、VPNコントロールサーバーに登録されるアカウント名が毎回空欄や、心当たりのない名前(他サービス連携によるものと思われる自動生成名)になってしまい、「could not register machine」「machine was previously registered with a different user」というエラーで弾かれ続けた。
VPNコントロールサーバーが発行する認可URLを、普段使っている通常のブラウザウィンドウで開いていたことが原因だった。そのブラウザにはauthentikへの古いログインセッション(Cookie)が残っており、認可URLを開いた瞬間、ログイン画面を経由せず、そのセッションのアカウントで自動的に認可されてしまっていた。VPNクライアント側でどれだけログアウト・再ログインを繰り返しても、ブラウザ側のセッションは別物なので解消されなかった。
認可URLは必ずブラウザの「プライベート/シークレットウィンドウ」で開くことで解決した。これにより毎回きちんとログイン画面が表示され、意図したアカウントで明示的にログインできるようになった。また、同一の機器(同じ物理デバイス)が過去に別アカウントで登録済みのまま残っていると、正しいアカウントで再登録しようとしても拒否されるため、管理コマンドで古い登録を削除してからやり直す必要があった。
教訓:ログイン絡みの「毎回同じ変な結果になる」系の不具合は、まずブラウザのセッションを疑う
サーバー側の設定を何度見直しても解決しない場合、原因がクライアント側(この場合はブラウザ)に残った状態であることは珍しくない。特に、SSOで一元管理されたログイン基盤では、意図せず「別のアカウントで自動ログインされ続けている」状態に気づきにくい。プライベートウィンドウを使う習慣をつけておくと、こうした切り分けが格段に楽になる。
9. 【重要】パスキーはブラウザ(Googleパスワードマネージャー)にも記憶される
パスキー導入後の動作確認中に、想定していなかった挙動に気づいた。スマートフォンでQRコードを読み取って登録したはずのパスキーが、PC側のブラウザだけで、スマートフォンを一切使わずにログインできてしまったのである。
「同期型パスキー」は、登録した端末以外でも使えてしまう
今回登録したパスキーは、Googleパスワードマネージャーに保存される「同期型パスキー」だった。同期型パスキーは、同じGoogleアカウントにサインインしているすべての端末(スマートフォンのChrome、PCのChrome等)に自動的に複製・同期される仕組みになっている。そのため、初回にスマートフォンでQRコードを読み取って登録した時点で、同じGoogleアカウントでサインインしているPC側のChromeにもパスキーのコピーが渡っており、2回目以降のログインでは、PC側のブラウザが自分の持つコピーだけで認証を完結できてしまった。
これはセキュリティ上どういう意味を持つか
パスキーは本来、「持っているもの」(Something you have)による認証要素を提供するはずである。しかし同期型パスキーの場合、実態としての「持っているもの」は物理的なスマートフォンではなく、「そのGoogleアカウントにサインインできる状態」そのものになっている。さらに、今回の動作確認では、PC側で保存済みパスキーを使う際に指紋・PIN等の追加確認が求められず、選択するだけで認証が完了した。つまり、次のような脅威モデルに対しては、当初期待していたほどの強度を持たない。
- PC・ブラウザプロフィールが盗まれた/乗っ取られた場合:スマートフォンを介さずに、そのままパスキーが使われてしまう
- Googleアカウント自体が侵害された場合:パスワードマネージャーごと複製されるため、パスキーも連鎖的に危険にさらされる
TOTPとの比較
| 比較観点 | TOTP(ワンタイムパスワード) | 同期型パスキー(今回の構成) |
|---|---|---|
| コード生成元 | スマートフォンの認証アプリ内で独立して生成される | Googleアカウントにサインインした全端末で共有される |
| 「PCが乗っ取られた」場合の耐性 | 高い(PCにはコードを生成する手段がない) | 低い(PC自体がそのまま認証器になってしまう) |
| フィッシング耐性 | 低い(偽サイトにコードを入力させて即座に中継される攻撃に弱い) | 高い(ドメインに紐付いているため偽サイトでは原理的に使えない) |
つまり「PCの物理的な乗っ取り」という脅威モデルではTOTPの方が確実に強く、「フィッシングサイトへの誘導」という脅威モデルではパスキーの方が強い。どちらが優れているかは一概には言えず、脅威モデル次第でトレードオフがあるという結論に至った。
今後の検討事項(未実施):端末固定型パスキーへの切り替え
Googleパスワードマネージャーに同期させず、特定の物理デバイス(スマートフォン本体等)だけにパスキーを留める「端末固定型(デバイスバウンド)」での登録に切り替えれば、毎回本当にスマートフォンでのQRコード読み取りが必須になり、TOTPと同等以上の「所持要素」としての強度を確保できる可能性がある。ただし、スマートフォン側の設定(同期をオフにする、または別のパスキー管理アプリを使う等)を確認しながらの再検証が必要なため、現時点では未実施。今後、時間のあるときに改めて検証する予定。
現時点の運用方針
上記の理由により、実運用ではTOTPを主に使用し、パスキーはあくまで追加の選択肢という位置づけで運用している。パスキーの方が速くログインできる場面では活用しつつ、強固な認証が必要な判断は行っていない。
10. トラブルシューティング
| 症状 | 確認・対処 |
|---|---|
| パスキー登録の画面が出てこない | HTTPS接続になっているか確認(WebAuthnはHTTPS必須。ローカルhttpでは動作しない) |
| 「スマホでQRコードを読み取る」選択肢が出ない | ブラウザをGoogle Chromeに切り替える。端末のBluetoothが有効か確認する(sudo systemctl status bluetooth) |
| 管理画面・ログイン画面自体に繋がらない(自宅Wi-Fi接続中) | RP IDはドメイン名必須のため、自宅LAN外(スマートフォンのテザリング等)から操作する |
| 「state has expired」と表示される | ログインフローのタイムアウト。新しいログインURLを再発行し、手早く進め直す |
| VPN(Headscale)側で毎回別アカウントとして登録されてしまう | 8章参照。認可URLは必ずプライベート/シークレットウィンドウで開く |
| スマホを使わずにPCだけでログインできてしまい違和感がある | 想定通りの挙動(9章参照)。同期型パスキーの仕様であり、故障や誤動作ではない |
11. まとめと次のステップ
パスキーの導入自体は完了し、authentikへのログイン・Headscale VPN経由での接続まで、実機でパスキー認証が機能することを確認した。一方で、今回の検証を通じて「同期型パスキーは、登録に使った端末以外でも認証が通ってしまう」という重要な特性が判明し、単純に「パスキーの方がTOTPより安全」とは言い切れないことが分かった。現時点ではTOTPを主軸に据え、パスキーは利便性重視の追加選択肢として運用している。
次のステップとして、端末固定型パスキーへの切り替え検証(9章参照)を今後の課題としている。あわせて、ロードマップPhase 1の完成条件である「全ユーザーへのMFA登録の義務化」も引き続き検討中である。