PHPサイトにTOTP二段階認証を導入する方法【QRコード登録・ログイン検証】

PHPサイトにTOTP二段階認証を導入する方法【QRコード登録・ログイン検証】

PHPで作ったWebサイトに、認証アプリの「6桁のワンタイムコード」を追加する方法を簡単にまとめます。TOTPは共通の秘密鍵と現在時刻からコードを生成する仕組みで、SMS送信や外部の認証サーバーは不要です。

導入の流れ

  1. 秘密鍵を生成:ユーザーごとにランダムな秘密鍵を作り、暗号化してサーバーに保存する。
  2. QRコードで登録:otpauth://形式のデータをQRコード化し、認証アプリに読み取らせる。
  3. 登録を確認:アプリに表示された6桁のコードを入力してもらい、正しい場合だけTOTPを有効化する。
  4. ログイン時に検証:パスワードに加えてTOTPコードを確認し、両方成功して初めてログインを完了する。

PHPでの実装例

自前で認証アルゴリズムを作らず、RFC 6238対応のOTPHPとQRコード生成ライブラリを利用します(PHP 8.1以上)。

composer require spomky-labs/otphp endroid/qr-code

1. 登録用の秘密鍵とQRコードを用意

<?php
require __DIR__ . '/vendor/autoload.php';

use OTPHP\TOTP;
use OTPHP\InternalClock;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;

$totp = TOTP::generate(new InternalClock(), 20)
    ->withIssuer('MySite')
    ->withLabel('[email protected]')
    ->withDigest('sha1')
    ->withDigits(6)
    ->withPeriod(30);

$secret = $totp->getSecret(); // 登録確認まで仮保存
$qr = new QrCode(data: $totp->getProvisioningUri());
$img = (new PngWriter())->write($qr)->getDataUri();
echo '<img src="' . $img . '" alt="認証アプリ登録用QR">';

このQRコードはログイン済みの本人だけに表示します。秘密鍵そのものが含まれるため、公開・外部QR生成サービスへの送信・キャッシュ保存は避けます。

2. 入力された6桁コードを検証

<?php
use OTPHP\TOTP;
use OTPHP\InternalClock;

// $secret はDBから復号して取得したユーザーの秘密鍵
// $code は利用者が入力した6桁の文字列
$totp = TOTP::createFromSecret($secret, new InternalClock());

if ($totp->verify($code)) {
    // 追加の安全確認を経て認証成功
} else {
    // 認証失敗
}

一般的な設定はSHA-1・6桁・30秒更新。認証アプリとサーバーは同じ秘密鍵と時刻を使用します。時計のずれを許容する場合も、前後1期間など必要最小限にします。

本番運用で必須の対策

  • 登録途中の鍵は無効のままにし、確認コードが正しい場合のみ有効化する。
  • 秘密鍵は暗号化してDBに保存。暗号化キーはDB・Web公開領域・Gitとは分離する。
  • コードの試行回数を制限し、使用済みの時間カウンターをDBトランザクションで一度だけ受理する(上のverify()だけでは再利用防止にならない)。
  • HTTPSを必須にし、パスワードとTOTPの両方が成功するまでログイン済みセッションを発行しない。
  • TOTPを解除・再設定するときは再認証を要求し、紛失時に備えて使い捨てのリカバリーコードを用意する。
  • 秘密鍵・入力コードをログに残さない。古いログインAPIからもTOTPを回避できないようにする。

要点:PHP側のTOTP計算は既存ライブラリで簡単に導入できます。重要なのは「秘密鍵の保護」「登録の本人確認」「ログイン処理への統合」「コードの再利用防止」です。上のコードは仕組みを示す最小例で、認証画面・DB処理・再試行制限などを追加して完成させます。

参考資料

コメントを残す