ブログ一覧

パスキーはドメインに縛られる — 独自ドメイン移行で管理者ログインできなくなる理由と対策(WebAuthn RP ID)

Cloudflare Workers でサイトを立ち上げるとき、まず *.workers.dev のステージング URL で動かして、仕上げに独自ドメインへ切り替える、という流れはよくあります。

このとき管理画面の認証がパスキー(WebAuthn)だと、確実に踏む落とし穴があります。workers.dev 上で登録したパスキーは、独自ドメインでは一切使えません。パスキーが唯一のログイン手段のままドメインを切り替えると、管理画面から完全に締め出されます。

本記事では、これがバグではなく WebAuthn の仕様(RP ID へのバインド)であること、締め出されたときの復旧経路、そして切り替え前にやっておくべきことをまとめます。

前提: パスキー認証を採用した Web アプリ全般の話です。例は Cloudflare Workers(workers.dev → 独自ドメイン)ですが、Vercel の *.vercel.app や Netlify の *.netlify.app からの移行でも同じことが起きます。

パスキーは「RP ID」に紐づく

WebAuthn のクレデンシャル(パスキー)は、登録時に RP ID(Relying Party ID) というドメインに紐づけられます(W3C WebAuthn 仕様 §5.1.1 rp.id)。認証時には「今アクセスしているオリジンのドメイン」と RP ID の対応が検証され、一致しないパスキーはそもそも候補として出てきません

これはフィッシング耐性の根幹です。example.com で登録したパスキーが examp1e.com(偽サイト)で使えたら意味がないので、ドメインへの束縛は仕様の意図そのものです。

つまり:

  • mysite.workers.dev で登録したパスキー → RP ID は mysite.workers.dev
  • サイトを mysite.com に移す → RP ID が違うので、そのパスキーは存在しないのと同じ

「移行」や「引き継ぎ」の仕組みはありません。新しいドメインで登録し直す、が唯一の道です。

workers.dev では「親ドメインに広く登録」もできない

「じゃあ RP ID を workers.dev にして広く効かせれば…」というのも封じられています。workers.devPublic Suffix List に登録されており、パブリックサフィックス(.com などと同格の扱い)を RP ID にすることはできません。*.workers.dev の各サブドメインは、それぞれ完全に独立した「サイト」です。

これも妥当な設計です。workers.dev 配下には無数の他人のサイトが同居しているので、そこをまたいで効くクレデンシャルが作れてはいけません。

締め出しの典型パターン

危険な手順はこうです。

  1. mysite.workers.dev でセットアップし、管理者パスキーを登録
  2. 独自ドメイン mysite.com に切り替え(DNS・ルーティング変更)
  3. mysite.com/admin にアクセス → パスキーが出てこない
  4. 「メールでログインリンクを送る」→ メールプロバイダを設定していないので送れない
  5. 詰み

厄介なのは 4 です。パスキー導入時は「メール基盤が不要」なのが利点なので、メール送信手段を持っていないことが多い。フォールバックのマジックリンク認証はコードとしては存在するのに、配送手段がなくて使えない、という状態になります。

なお、データベースを直接触っての復旧も基本的にできません。まともな認証システムはログイントークンをハッシュ化して保存するので、DB からログイン URL を再構成することは不可能です。初期セットアップウィザードも「ユーザーが 1 人でも存在したら再実行不可」のようにガードされているのが普通で、これを無理に通そうとすると既存データの破壊とセットになります。

復旧: メールの配送先を Slack にすげ替える

締め出された状態から実際に復旧できた手順は、「メール送信をチャット Webhook への送信に差し替える」でした。

  1. 認証システムのメール配送フックに、Slack Incoming Webhook へ POST するプロバイダを差し込む(メール配送を差し替え可能なプラグイン機構があるシステムなら、これができます)
  2. マジックリンクの送信をリクエスト → ログインリンクが Slack に届く
  3. リンクからログイン → 新ドメインでパスキーを登録し直す

メールサーバーの契約や DNS 設定(SPF/DKIM)なしで即日復旧できるのがこの方法の利点です。ただし Slack にワンタイムトークン付き URL を流すときは、リンクプレビューのクローラーがトークンを先に消費するという別の罠があります。これは「Slack に送ったログインリンクが、人間が踏む前に無効化される」にまとめました。

予防: ドメイン切り替えの正しい順序

締め出されてから復旧するより、切り替え前に 10 分使う方が圧倒的に安いです。

  1. 切り替え前に、パスキー以外のログイン経路を 1 本確保する(メールプロバイダ設定、またはチャット Webhook 配送。動作確認まで)
  2. ドメインを切り替える
  3. アプリのサイト URL 設定を新ドメインに更新する。多くの実装では、この設定が新規パスキー登録時の RP ID の元になります。ここを忘れて手順 4 に進むと、新パスキーが旧ドメインに紐づくという二次災害が起きます
  4. フォールバック経路でログインし、新ドメインでパスキーを登録
  5. 旧ドメインのパスキーを削除(もう何にも使えないので)

まとめ

  • パスキーは RP ID(ドメイン)に束縛される。ドメインが変わったら登録し直し以外の道はない。これは仕様であり、フィッシング耐性の代償
  • workers.dev はパブリックサフィックスなので、親ドメイン単位で広く効かせる逃げ道もない
  • パスキーを唯一のログイン手段にしたままドメインを切り替えると締め出される。切り替え前にフォールバック経路(メールまたはチャット Webhook 配送)を確保する
  • 新パスキーの登録前に、サイト URL 設定を新ドメインへ更新する

参考リンク