パスキー 2025 - パスワードレス認証が主流に

2026.01.12

パスキーが主流に

2025年、パスキーは実験的な技術から主流の認証方式へと進化しました。パスワードの脆弱性を根本から解決する技術として、消費者と企業の両方で急速に採用が進んでいます。

2025年の採用状況

指標数値
パスキー保有者69%
認知度75%
上位100サイトでの対応48%
ログイン成功率93%(従来63%)
企業での導入・計画中87%

パスキーとは

パスキーはFIDO2/WebAuthn標準に基づく公開鍵暗号方式の認証技術です。

パスワードとの違い

パスワード認証:
  ユーザー → パスワード送信 → サーバーで照合
  問題: フィッシング、漏洩、使い回し

パスキー認証:
  ユーザー → 秘密鍵で署名 → サーバーで公開鍵検証
  利点: 送信される秘密がない、フィッシング不可能

技術スタック

graph TB
    subgraph FIDO_Alliance["FIDO Alliance"]
        subgraph FIDO2
            WebAuthn["WebAuthn<br/>(W3C標準)"]
            CTAP2["CTAP2<br/>(認証器プロトコル)"]
        end
    end

実装例

JavaScript(WebAuthn API)

// パスキー登録
async function registerPasskey() {
  const challenge = await fetch('/api/auth/challenge').then(r => r.json());

  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: base64ToBuffer(challenge.challenge),
      rp: {
        name: "My Application",
        id: window.location.hostname
      },
      user: {
        id: base64ToBuffer(challenge.userId),
        name: "user@example.com",
        displayName: "User Name"
      },
      pubKeyCredParams: [
        { type: "public-key", alg: -7 },   // ES256
        { type: "public-key", alg: -257 }  // RS256
      ],
      authenticatorSelection: {
        authenticatorAttachment: "platform",  // デバイス内蔵
        residentKey: "required",              // Discoverable
        userVerification: "required"          // 生体認証
      },
      timeout: 60000
    }
  });

  // サーバーに登録
  await fetch('/api/auth/register', {
    method: 'POST',
    body: JSON.stringify({
      id: credential.id,
      rawId: bufferToBase64(credential.rawId),
      response: {
        attestationObject: bufferToBase64(
          credential.response.attestationObject
        ),
        clientDataJSON: bufferToBase64(
          credential.response.clientDataJSON
        )
      }
    })
  });
}

// パスキーログイン
async function loginWithPasskey() {
  const challenge = await fetch('/api/auth/challenge').then(r => r.json());

  const assertion = await navigator.credentials.get({
    publicKey: {
      challenge: base64ToBuffer(challenge.challenge),
      rpId: window.location.hostname,
      userVerification: "required",
      timeout: 60000
    }
  });

  const response = await fetch('/api/auth/login', {
    method: 'POST',
    body: JSON.stringify({
      id: assertion.id,
      rawId: bufferToBase64(assertion.rawId),
      response: {
        authenticatorData: bufferToBase64(
          assertion.response.authenticatorData
        ),
        clientDataJSON: bufferToBase64(
          assertion.response.clientDataJSON
        ),
        signature: bufferToBase64(
          assertion.response.signature
        )
      }
    })
  });

  return response.json();
}

サーバーサイド(Node.js)

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';

// 登録オプション生成
app.post('/api/auth/register/options', async (req, res) => {
  const user = await getUser(req.session.userId);

  const options = await generateRegistrationOptions({
    rpName: 'My Application',
    rpID: 'example.com',
    userID: user.id,
    userName: user.email,
    userDisplayName: user.name,
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'required',
      userVerification: 'required'
    }
  });

  req.session.challenge = options.challenge;
  res.json(options);
});

// 登録検証
app.post('/api/auth/register/verify', async (req, res) => {
  const verification = await verifyRegistrationResponse({
    response: req.body,
    expectedChallenge: req.session.challenge,
    expectedOrigin: 'https://example.com',
    expectedRPID: 'example.com'
  });

  if (verification.verified) {
    await saveCredential(req.session.userId, {
      credentialID: verification.registrationInfo.credentialID,
      credentialPublicKey: verification.registrationInfo.credentialPublicKey,
      counter: verification.registrationInfo.counter
    });
  }

  res.json({ verified: verification.verified });
});

NIST認定

2025年7月31日に最終化予定のNIST SP 800-63-4では、パスキー(syncable authenticators)が正式に認定。

Authenticator Assurance Level 2 (AAL2)
├── パスキーが正式に認定
├── 企業・政府での利用が可能に
└── 規制要件を満たす認証方式として承認

導入の簡素化

2023年: 6ヶ月の移行プロジェクト
2025年: 2-3スプリントで完了

理由:
・IAMプラットフォームのドロップインウィジェット
・組み込みWebAuthnライブラリ
・自動フォールバック戦略
・管理レベルのアテステーションサポート

Passkey Pledge

FIDO AllianceのPasskey Pledgeには、20日間で100以上の組織がコミット。

参加企業例:
・Apple、Google、Microsoft
・主要な金融機関
・Eコマースプラットフォーム
・クラウドサービスプロバイダー

SMS OTPの廃止

UAEの先行事例

UAEでは2026年3月までに金融機関でのSMS/Email OTPを廃止する指令を発出。

uae_directive:
  deadline: 2026-03
  affected: 全ライセンス金融機関
  required: パスキーまたは同等のフィッシング耐性認証
  rationale: SIMスワップ攻撃、フィッシングの増加

ユーザー体験の比較

パスワード認証

1. ユーザー名入力
2. パスワード入力(覚えていれば)
3. 間違えたらリセット
4. MFAコード入力
5. ログイン完了

問題: 複雑、時間がかかる、63%の成功率

パスキー認証

1. 「パスキーでログイン」をクリック
2. 生体認証(指紋/顔)
3. ログイン完了

利点: シンプル、高速、93%の成功率

実装のベストプラクティス

段階的導入

Phase 1: 新規ユーザー向けパスキー登録オプション
Phase 2: 既存ユーザーへの移行促進
Phase 3: パスワードレスをデフォルトに
Phase 4: パスワード認証の段階的廃止

フォールバック戦略

// 段階的なフォールバック
async function authenticate(user: User): Promise<AuthResult> {
  // 1. パスキーを優先
  if (await hasPasskey(user) && await webAuthnSupported()) {
    try {
      return await authenticateWithPasskey(user);
    } catch (e) {
      console.log('Passkey failed, falling back');
    }
  }

  // 2. セキュリティキー
  if (await hasSecurityKey(user)) {
    try {
      return await authenticateWithSecurityKey(user);
    } catch (e) {
      console.log('Security key failed, falling back');
    }
  }

  // 3. TOTP(最後の手段)
  return await authenticateWithTOTP(user);
}

市場予測

2025年: パスワードレス認証市場 $24.1B
2030年予測: $55.7B(CAGR 18.24%)

参考: FIDO Alliance - World Passkey Day 2025

まとめ

2025年、パスキーはパスワードレス認証のスタンダードとして確立されました。93%のログイン成功率、フィッシング耐性、NISTによる公式認定など、セキュリティと利便性の両面で従来の認証方式を大きく上回っています。SMS OTPの廃止トレンドも相まって、パスキー導入は今後避けられない流れとなるでしょう。

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

LINEで無料相談する
← 一覧に戻る