【導入事例付き】Laravel + WebAuthnで実現するFIDO認証
セキュリティと操作性を両立する、FIDO認証導入の実践ガイド
2025-11-05

目次
はじめに
Laravel + laragear/webauthnを使ったFIDO認証の導入について解説します。FIDOの基礎知識から技術的な実装方法まで順に紹介します。
FIDO認証とは?
パスワードを使わずに本人認証ができ、サービスにログインなどができる手段の1つです。
ユーザーがもつデバイス側で、生体認証などにより本人認証を行い、秘密鍵を使って署名つき認証結果をサーバー側に送ります。サーバー側は公開鍵で署名を検証し認証が行われます。手段には、ユーザーが持つスマホの指紋認証や、MACなどはそのままPCで指紋での本人認証が可能です。
WebAuthnは、W3C(World Wide Web Consortium)が策定したWeb認証の標準規格です。2019年に正式となり、現在は主要なWebブラウザ(Chrome、Firefox、Safari、Edge)でサポートされており、安全な認証システムを構築できます。
技術的な仕組みとして、FIDO認証ではユーザー端末上で暗号化された安全な鍵ペア(公開鍵・秘密鍵)を生成します。公開鍵がサーバーに登録され、秘密鍵は端末内に保存されます。ログインなどの認証時には、それらのキーで署名したものを検証することで本人認証を実現します。
なぜ、今、FIDO認証なのか?
企業には外部に公開しているサービスもあれば、内部だけで使用する社内システムも複数あると思います。しかし、ユーザーが増え、セキュリティを気にし出すと以下の問題に当たることがあります。
- パスワードを複雑にすると、覚えにくくなる
- 定期的な変更を求めると、更新が面倒になる。メモに書いて放置される
- 自社のセキュリティ強化をしても使い回したパスワードが他サイトから流出するリスクがある
この矛盾を解決する技術として、FIDO認証が注目されています。FIDO認証は今では大手サービスでは導入が進むほど普及しています。このコラムを読んでいる方も、銀行や証券会社のウェブサイトなどで一度は見かけたことがあると思います。
【実装事例】Laravel + laragear/webauthnでのFIDO認証導入
今回はデモサイトの仕様上「IDとパスワード」認証できるアカウントに追加のログイン認証方法としてFIDO認証をできるように導入しました。FIDOの仕様や技術を理解することで、2段階認証に応用したり、パスワードレスの認証にすることも可能です。
前提要件
- PHP 8.0以上
- Laravel 10.x
- laragear/webauthn パッケージ(https://github.com/Laragear/WebAuthn)
- MySQL
パッケージインストールと初期設定
まずは必要なライブラリをインストールしましょう。
bash
composer require laragear/webauthn
php artisan vendor:publish --provider="Laragear\WebAuthn\WebAuthnServiceProvider"
php artisan migrate
認証情報を保存するmigrationファイルはこのようになっています。
return WebAuthnCredential::migration()->with(function (Blueprint $table) {
//カスタマイズしたければここに渡す
});
### 実際にはこのようなカラムが作成されます。
id - WebAuthn認証情報の一意識別子(主キー)
authenticatable_type - 認証可能なモデルの型
authenticatable_id - 認証可能なモデルのID
user_id - ユーザーのUUID(匿名化されたユーザー識別子)
alias - 認証情報のフレンドリー名(例:「デバイス認識用の名前」、nullable)
counter - クローン検出用のカウンター(unsignedBigInteger、nullable)
rp_id - 認証情報を作成したRelying PartyのID
origin - 認証情報が作成された場所のURL
transports - 公開鍵の伝送方法(JSON配列、nullable)
aaguid - 認証器のタイプやプロパティ(UUID、nullable)
public_key - チャレンジ検証用の公開鍵(暗号化済みテキスト)
attestation_format - 公開鍵の証明形式(デフォルト:None)
certificates - 証明書チェーン(JSON配列、nullable)
disabled_at - 認証情報の無効化日時(nullable)
複雑なことはなくこの1テーブルで実装ができます。
自社サービスのようにカスタマイズする場合は、migrationのclosureの中にカラム設定を渡してあげることでカスタマイズしたテーブルを作成できます。
認証情報の登録フロー
登録の処理は以下のようになっています。手順も多く複雑だと思いますが、大体をライブラリが吸収してくれているので実装自体は比較的安易です。
ゼロから実装しようとするととても大変です。また、ログイン回りはセキュリティで重要な部分になるためライブラリを使う方が良い判断です。
詳しい仕様などはこちらに書いてあります。

登録ページはデモサイトにて公開しておりますので、ぜひ操作してみてください。
デモサイトはこちら
※ユーザー情報が必要なため、ログインすることで利用することができます。
ログインIDおよびパスワードは以下の通りです。
ID: demo_admin
PW: DemoAdmin!2025

チャレンジレスポンス方式
WebAuthnの登録プロセスは、チャレンジレスポンス方式に基づいています。サーバー側でランダムなチャレンジを生成し、クライアント側でそのチャレンジに対する暗号学的な応答を生成します。
この仕組みにより、ネットワーク上でやり取りされる情報から秘密鍵が推測されることがなく、セキュアな認証が実現されます。
実装は以下のようになります。
php
class WebAuthnRegisterController
{
/**
* Returns a challenge to be verified by the user device.
*/
public function options(AttestationRequest $request): Responsable
{
return $request
->fastRegistration()
->toCreate();
}
}
public function toCreate(): Responsable
{
return $this->container
->make(AttestationCreator::class)
->send($this->attestation())
->then(static function (AttestationCreation $creation): Responsable {
return $creation->json;
});
}
登録フローで重要なのは、エラーハンドリングです。WebAuthn APIはさまざまな理由で失敗する可能性があるため、これらの状況を適切にハンドリングし、ユーザーにとって分かりやすいフィードバックを提供することが重要です。
古いブラウザや対応していないデバイス、認証操作のキャンセルやタイムアウト、スマホなどの通信環境が不安定な時の通信エラーや遅延などを考慮する必要があります。

これはFIDOだけでなく、サービス全体としてエラーハンドリングは重要な項目です。忘れがちな部分なのでシステムを構築する際には注意が必要です。
ログインフローの実装

認証プロセスの流れ
こちらのデモサイトで確認できます。
デモサイトはこちら

ログインフローは登録フローよりも複雑で、セキュリティ上の考慮事項も多くなります。基本的な流れは以下の通りです。
- 認証オプション生成:サーバー側でチャレンジとオプションを生成
- デバイス認証:ユーザーが生体認証やPINで本人確認
- 署名検証:サーバー側で署名とデータの整合性を検証
- ログイン完了:セッション確立とリダイレクト
署名検証の実装
最も重要なのが署名検証プロセスです。以下の要素を厳密にチェックする必要があります。
- クライアントデータの検証:オリジン、タイプ、チャレンジの確認
- 認証データの検証:RPIDハッシュ、カウンターの確認
- 署名の検証:保存されている公開鍵による暗号学的検証
特にオリジン検証は重要で、フィッシングサイトからの不正な認証試行を防ぐ最後の砦となります。設定されたオリジンと受信したオリジンが完全に一致することを確認する必要があります。
php
$clientData = json_decode(base64_decode($request->input('response.clientDataJSON')), true);
// 基本的な検証: オリジンとタイプのチェック
if ($clientData['type'] !== 'webauthn.get') {
Log::warning('Invalid WebAuthn type', ['type' => $clientData['type']]);
return $this->errorResponse(__('webauthn.login.failed'), 422);
}
if ($clientData['origin'] !== config('app.url')) {
Log::warning('Invalid WebAuthn origin', [
'received' => $clientData['origin'],
'expected' => config('app.url')
]);
return $this->errorResponse(__('webauthn.login.failed'), 422);
}
カウンター値によるリプレイ攻撃対策
WebAuthnでは、リプレイ攻撃を防ぐためのカウンター機能があります。認証デバイスは使用回数をカウントし、毎回インクリメントされた値を送信します。
サーバー側では、前回保存されたカウンター値よりも大きい値が送信されることを確認し、認証成功時にデータベースのカウンター値を更新することを推奨します。
フロントの実装のポイント
WebAuthn APIは非同期処理が中心となるため、適切なエラーハンドリングとユーザーフィードバックが重要です。特に、認証処理中のローディング表示や、タイムアウト処理、エラー時の分かりやすいメッセージ表示は、ユーザビリティに大きく影響します。
また、WebAuthn APIの戻り値は複雑な構造を持つため、サーバー側で処理しやすい形式に変換する処理も必要です。
実装方法は以上になります。
まとめ
Laravelを用いて簡易的にFIDO認証を実装してみました。
これまで通りのID+パスワードの認証で課題だったパスワード忘れやセキュリティの課題はFIDO認証を入れることである程度解決できると思います。
さいごに
FIDO認証は「安全で、使いやすいログイン体験」を実現する技術です。しかし、実装は認証フロー・署名検証・エラーハンドリングなど、セキュリティとユーザビリティの両立が求められる領域でもあります。
当社フレシット株式会社では、Laravelを中心としたフルスクラッチ開発により、FIDOやWebAuthnをはじめとする高度な認証システムを、業務やサービスの特性に合わせて設計・構築しています。
単なる技術導入ではなく、「どの業務に」「どんなユーザー体験で」FIDOを活かすか──そこから一緒に考え、最適な認証基盤をつくりあげることが私たちの強みです。
自社サービスへのFIDO認証導入やセキュリティ強化をご検討の際は、ぜひ当社にご相談ください。
監修者プロフィール
フレシット株式会社 代表取締役 増田 順一
柔軟な発想でシステム開発を通して、お客さまのビジネスを大きく前進させていくパートナー。さまざまな業界・業種・企業規模のお客さまの業務システムからWEBサービスまで、多岐にわたるシステムの開発を手がける。一からのシステム開発だけでは無く、炎上案件や引継ぎ案件の経験も豊富。システム開発の最後の砦、殿(しんがり)。システム開発の敗戦処理のエキスパート。

公式Xアカウントはこちら