要約(パスキーとは):パスキーは公開鍵暗号と端末の生体認証を使い、パスワードなしでログインできるフィッシング耐性の高い多要素認証方式です。サイトのドメイン(RP ID)に結びつき、だましサイトでは使えないため攻撃に強いのが特徴です。

1. パスキーとは(何が新しいか)
1-1. パスワードと違う点:共有秘密じゃなく公開鍵暗号で認証する
パスキー(Passkeys)は、FIDO2/WebAuthn をベースにしたパスワードに代わる認証手段です。
一番の違いは「共有秘密かどうか」です。
- パスワード:
- 「サーバとユーザーが同じ秘密(パスワード)を知っている」前提です。
- 漏えいすると、同じパスワードを使っている他サービスにも攻撃が波及しやすいです。
- パスキー:
- 公開鍵暗号を使います。
- サーバは「公開鍵」を持ち、クライアント(端末)は「秘密鍵」を安全な領域に保存します。
- ログイン時には「秘密鍵で署名」→「公開鍵で検証」という流れです。
サーバ側には「秘密」そのものが保存されないため、DB漏えい時のインパクトを大きく減らせるのが大きなポイントです。
1-2. “フィッシング耐性”の理由:RP ID / origin に結びつく
パスキーが「フィッシング耐性MFA」と呼ばれる理由は、認証情報がサイト(ドメイン)に結びついているからです。
- パスキーは「RP ID(Relying Party ID)」=だいたいドメイン名に紐づきます
- ブラウザやOSは、今表示している origin / RP ID と合っていないパスキーは使わせません
そのため、攻撃者が
https://example.comに似せたhttps://examp1e.com(L→1など)を用意しても- ユーザーのパスキーは
example.com向けのものなので、examp1e.comでは使えない
という仕組みになっています。「だましサイトでパスキーを抜く」という攻撃が難しいのが大きな強みです。
1-3. WebAuthn/FIDO2とPasskeysの関係(用語整理)
用語がややこしいので、一度整理しておきます。
- FIDO2:
- FIDO Alliance が定めた認証の枠組み全体の名前です。
- WebAuthn(Web Authentication):
- ブラウザ側の仕様(W3C標準)です。
navigator.credentials.create()/get()などのAPIで FIDO 認証器を使う部分を定義しています。- Passkeys:
- FIDO2/WebAuthn をユーザーに分かりやすくまとめた「体験の名前」に近いです。
- Apple や Google、Microsoft がクラウド同期やクロスデバイスログインなどを含めて提供しています。
ざっくり言うと、「WebAuthn/FIDO2 の上に、OSおよびクラウドが UX と同期機能を足したもの」がパスキーだと捉えると分かりやすいです。
2. どんな種類がある(同期・端末固定・持ち運び)
2-1. 同期パスキー(クラウド同期)と端末固定の違い
大きく見ると、パスキーには次の2タイプがあります。
- 同期パスキー(syncable passkeys):
- Apple ID や Google アカウントに紐づいて、クラウド経由で同期されます。
- iPhone・iPad・Mac 間でシームレスに使える、Android・Chrome 間で使える、などの体験です。
- 端末固定(device-bound)パスキー:
- YubiKey や一部のプラットフォーム認証器など、「特定の端末・デバイスに固定される」タイプです。
- クラウド同期されないため、物理的にそのデバイスを持っていることが認証の条件になります。
エンタープライズ用途では、「高リスク操作だけ device-bound を必須にする」といった使い分けも検討対象になります。
2-2. クロスデバイスログイン(QR で近接端末を使う)とは
PCブラウザで「スマホでパスキー認証する」体験を見たことがあるかもしれません。これがクロスデバイスログインです。
- PCでログイン画面を開く
- QRコードが表示される
- スマホでQRを読み取り、スマホ側で生体認証(指紋/Face ID)
- PCのブラウザ側に結果が返ってログイン完了
これは「スマホがパスキーの認証器として動き、近接通信(Bluetoothなど)でPCとやり取りしている」イメージです。
ユーザーにとっては、「スマホをかざしてログインできる」便利なパターンですが、一部の企業ポリシーではスマホ利用を制限していることもあるため、運用設計で考慮が必要です。
2-3. 対応環境の押さえどころ
実際に導入するときは、「どのOS+ブラウザ+アカウント環境でパスキーが動くか」を確認する必要があります。
- Android + Chrome + Google アカウント
- iOS / iPadOS + Safari + Apple ID(iCloud キーチェーン)
- Windows + Edge/Chrome + Microsoft アカウント など
まずは「自社のユーザーがよく使う環境」を洗い出し、そこだけでも体験が破綻しないかを確認しておくと、導入後の問い合わせを減らせます。
3. 導入のユースケース(何が嬉しいか、どこで詰まるか)
3-1. 置き換え対象:ログイン、ステップアップ認証、再認証
パスキーは、次のような場面で特に効果を発揮します。
- メインログイン:
- 従来の「ID+パスワード」を「ID+パスキー」に置き換える。
- パスワード忘れ・リセット地獄を減らせます。
- ステップアップ認証:
- ログイン済みでも、「支払い」「権限変更」「APIキー表示」などの前に、追加のパスキー認証を要求する。
- 重要操作の再認証:
- 長時間操作していない場合や、リスクが高い操作の直前で、パスキーによる再認証を行う。
いきなり「全ログイン」を置き換えるより、まずは「支払い前の再認証」など限定的な箇所から試すと、安全側に倒しつつ UX も確認しやすいです。
3-2. 体験の差:create と get の UI はOS/ブラウザで変わる
パスキーの実装では、クライアント側で主に次の2つの呼び出しを行います。
navigator.credentials.create():パスキーの登録(create)navigator.credentials.get():パスキーによる認証(get)
JavaScript のイメージコードは次のようになります。
// 例:登録(create) フローのクライアント側イメージ
async function registerPasskey() {
// 1. サーバから公開鍵クレデンシャル作成オプションを取得
const res = await fetch("/webauthn/register/options", {
method: "POST",
credentials: "include",
});
const options = await res.json();
// 2. バイナリ系をArrayBufferに変換(略)
// 3. WebAuthn APIを呼び出し
const credential = await navigator.credentials.create({
publicKey: options,
});
// 4. 結果をサーバに送信
const res2 = await fetch("/webauthn/register/verify", {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(credential),
});
return res2.ok;
}
`
このコードでは、まずサーバから登録用のオプション(challenge や RP ID などを含む)を取得し、それを navigator.credentials.create に渡して OS/ブラウザのネイティブ UI を起動しています。戻ってきた credential をサーバに送り、検証が通ればパスキー登録完了という流れになります。「UIは OS/ブラウザ任せで、アプリ側はオプションと結果のやりとりに集中する」のが基本的な考え方です。
ここで注意したいのは、「ユーザーに見える UI は OS/ブラウザごとに違う」という点です。
iPhone と Windows と Android では、見た目も文言も微妙に違うので、ヘルプドキュメントを書くときはスクリーンショットを複数用意する必要があります。
3-3. “全員が使えるわけじゃない”現実
技術的には魅力的ですが、現実には次のような制約があります。
- 古い端末やブラウザではパスキー非対応
- 企業ポリシーで「個人の Apple ID / Google アカウント」が禁止されている
- コールセンターや店舗などの「共有端末」で使いづらい
そのため、運用設計としては「パスキーを必須にする前に、どのくらいのユーザーが利用可能か」を把握することが重要です。
利用状況をログに残し、「全ログインのうち何%がパスキー利用か」を見ながら、段階的に必須化するのが現実的です。
4. 実装設計の要点(2年目が押さえるべき最低ライン)
4-1. RP(Relying Party)設計:RP ID、サブドメイン、環境の切り方
WebAuthn では、RP ID が非常に重要です。通常は example.com のようなドメインで指定します。
- RP ID はサブドメインをまとめる動きをします(
app.example.comでもapi.example.comでも同じ RP ID を使いたいなど)。 - 逆に、
stg.example.comとexample.comを分けたい場合は、RP ID や origin の設計を慎重に行う必要があります。
ステージング環境で stg.example.com を使い、本番は example.com、RP ID はそれぞれ別にする、といった設計にしておくと、「stg で登録したパスキーが本番に影響する」といった事故を防げます。
4-2. 登録/認証フローの実装ポイント
サーバ側で押さえておきたいポイントは次のとおりです。
- challenge:
- 毎回ランダムな値を生成し、一度きりで使い捨てます。
- DBやセッションに保存し、「戻ってきたレスポンスの challenge と一致するか」を検証します。
- ユーザー検証(User Verification, UV):
- 指紋・Face ID・PIN などでユーザー本人を確認する設定です。
- 重要な操作では
requiredにする、などのポリシー設定が必要です。 - credential ID:
- 登録されたパスキーを識別するIDで、ユーザーごとに複数持てます(スマホ・PCなど)。
- DBにユーザーIDと credential ID の組み合わせで保存し、認証時に参照します。
// 擬似コード:登録用のオプション生成(サーバ側)
function generateRegistrationOptions(user) {
const challenge = randomBytes(32);
// DBやセッションにchallengeを保存して、後で検証に使う
saveChallenge(user.id, challenge);
return {
challenge,
rp: { id: "example.com", name: "Example Service" },
user: {
id: user.id,
name: user.email,
displayName: user.name,
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 }, // RS256 など
],
authenticatorSelection: {
userVerification: "preferred", // or "required"
},
timeout: 60000,
};
}
このコードでは、ユーザーごとにランダムな challenge を発行し、RP 情報(どのサービスか)、ユーザー情報(id/name)、利用する暗号アルゴリズムなどをまとめて返しています。クライアント側はこのオプションをそのまま navigator.credentials.create に渡すことで、ブラウザに「この RP で、このユーザー向けのパスキーを作ってください」と依頼しています。サーバ側では challenge を保存しておき、レスポンスの検証時に「このリクエストは本当に自分が出した challenge に対するものか」を確認することで、中間者攻撃やリプレイ攻撃を防いでいます。
4-3. 既存認証との整合:パスワード・2FA・セッション
いきなり「パスワード廃止」は現実的ではないので、多くの場合は次のような組み合わせになります。
- パスワード+パスキー併用(ユーザーがどちらかを選べる)
- パスワード + パスキー(2要素として両方要求)
- パスキー + TOTP / SMS(さらに強い多要素が必要な場合)
セッション管理では、
- 「パスキーでログインしたセッション」と「パスワードでログインしたセッション」に違いを持たせるか
- 「パスキーで認証したら、一定時間は高信頼なセッションとして扱う」などの区別をつけるか
といった設計を考える必要があります。
このあたりは SSO 基盤や既存のIDプロバイダとの連携も絡んでくるため、まずは「どういう状態を高信頼と見なすか」を紙に描き出してから実装を進めると迷いにくくなります。
5. 運用が本体:移行・復旧・サポート設計
5-1. 段階移行プラン:併用 → 既定(推奨) → 条件付き強制
パスキー導入は「いきなり全員必須」ではなく、段階的に行うのが安全です。典型的なプランは次のようになります。
- 併用フェーズ:
- パスワードログインはそのまま、パスキーはオプションとして登録・利用可能にする。
- 利用状況をログに取り、「どれくらい使われているか」を観測します。
- 既定(推奨)フェーズ:
- ログイン画面のデフォルトを「パスキーにする」(パスワードはリンクから選べる形)
- 新規登録ユーザーにはパスキー登録を強く案内する。
- 条件付き強制フェーズ:
- 管理者操作や高額決済など、一部の高リスク操作ではパスキー必須にする。
- 将来的に「一定条件を満たしたユーザーはパスワード廃止」などを検討。
このように段階を分けておくと、「使えるユーザーが増えてから強制」に自然と持っていけます。
5-2. 端末紛失/買い替え時の復旧:代替ログインを残す
パスキー運用で一番大事なのは「端末をなくしたときにどうするか」です。
- 同期パスキーの場合:
- クラウド(Apple ID / Google アカウント)にログインできれば、別の端末で復旧可能。
- 逆にそのクラウドアカウントが乗っ取られると危険、という側面もあります。
- device-bound の場合:
- 物理デバイス(YubiKeyなど)をなくした場合の代替手段が必要。
現実的には、次のような「代替ログイン」を残しておく必要があります。
- メール認証+本人確認フロー
- SMS / 電話によるサポート(ヘルプデスク経由)
- バックアップコード(使い切りの一時コード)
パスキーの安全性が高いほど、この「代替手段」が攻撃者の狙い目になります。
そのため、「代替の方が弱くなりすぎないようにする」ことも運用設計の重要ポイントです。
5-3. ヘルプデスク運用:本人確認とパスキー再登録
ヘルプデスク(サポート窓口)向けには、次のような手順を決めておくと安心です。
- 本人確認の手順:
- 登録済みメールアドレス・電話番号への確認コード送信
- 別チャネル(電話・社内チャット)での確認
- アカウント回復の流れ:
- 古いパスキーをすべて無効化する
- 一時的なログインURLやバックアップコードを発行する
- ログイン後、新しいパスキーの登録をガイドする
- ログの残し方:
- 誰が、いつ、どのアカウントに対して回復処理を行ったか
ここをきちんと決めておかないと、「サポートに電話をかければ、だれでもアカウントを取り戻せる」という弱点になってしまうので、技術的なパスキーの強さを下げないように注意が必要です。
6. セキュリティ観点のチェックリスト
6-1. フィッシング耐性は万能ではない
パスキーはフィッシング耐性が高い一方で、万能ではないことも押さえておく必要があります。
- 端末自体がマルウェアに乗っ取られている場合:
- 攻撃者は正しい UI を経由してパスキーを使ってしまうかもしれません。
- クラウドアカウント(Apple ID / Google アカウント)の乗っ取り:
- 同期されているパスキーがまとめて危険になる可能性があります。
- アカウント回復フローの弱さ:
- 「サポートに電話したら簡単にリセットできる」状態だと、そこが攻撃ベクトルになります。
フィッシングに強くなった分、エンドポイントと回復フローの重要性が上がる、という意識をチームで共有しておくことが大切です。
6-2. 監査ログ:登録/削除/失敗をきちんと残す
運用でトラブルシュートや不正検知をしやすくするために、次のようなログを残しておきます。
- パスキー登録:
- いつ、どのユーザーが、どの種類の認証器(プラットフォーム/セキュリティキー)を登録したか
- パスキー削除:
- 削除したのは本人か、ヘルプデスクか
- 認証失敗:
- 失敗理由(challenge 不一致、RP ID 不一致、ユーザーキャンセルなど)
これらを「ユーザーID」「IP」「User-Agent」「デバイス種別」などと紐づけておくと、
「この時間帯に特定IPから大量の失敗があった」といった検知にも使えます。
6-3. ポリシー:強制条件と例外の管理
最後に、パスキーをどこまで強制するかのポリシー例です。
- 強制条件:
- 管理者画面へのログイン
- 高額の送金・決済
- APIキーの表示・再発行
- リスクベース:
- 異常なIP / 地域からのアクセス
- 普段と違う端末・ブラウザからのログイン
- 例外管理:
- どうしてもパスキーが使えないユーザー向けに、別のMFA(TOTPなど)を用意する
- 例外申請や有効期限を決めて、永続的な例外が増えすぎないようにする
「とりあえず全員必須」にすると運用が破綻しがちなので、「どの操作から必須にしていくか」をポリシーとして設計しておくことが鍵です。
7. まとめ
7-1. パスキーは「認証方式」より「運用設計」が勝負
パスキーは、技術的にはとても強力な認証方式ですが、運用設計が整っていないと、
「使える人だけが使う」「サポートに負荷が集中する」状態になりがちです。
- どのユースケースから導入するか(再認証 → ログイン → 強制化)
- どの環境で動くか(OS・ブラウザ・アカウント条件)
- 端末紛失・買い替え・アカウント回復をどう扱うか
この3つを、実装と並行して決めていくことが重要です。
7-2. 失敗しない導入順番
最後に、比較的失敗しにくい導入順の一例をまとめます。
- 重要操作の再認証にパスキーを入れる(ステップアップ認証)
- 利用状況を見ながら、通常ログインの「推奨」にする(UI上はパスキーを前面に)
- 管理画面や高リスク操作から、条件付きでパスキーを必須にする
この順番であれば、「いきなり全員必須」「復旧フローが追いつかない」といった事態を避けながら、
少しずつフィッシング耐性MFAの恩恵を広げていくことができます。
8. 参考リンク
- FIDO Alliance: Passkeys
https://fidoalliance.org/passkeys/ - FIDO Alliance: Passkey Implementation Overview
https://fidoalliance.org/implement-passkeys-overview/ - W3C: Web Authentication (WebAuthn) Level 2
https://www.w3.org/TR/webauthn-2/ - W3C: Web Authentication (WebAuthn) Level 3
https://www.w3.org/TR/webauthn-3/ - Google Developers: Passkey supported environments
https://developers.google.com/identity/passkeys/supported-environments - Apple Support: iPhoneでパスキーを使う
https://support.apple.com/guide/iphone/use-passkeys-to-sign-in-to-websites-and-apps-iphf538ea8d0/ios