PHPで作ったWebサイトに、認証アプリの「6桁のワンタイムコード」を追加する方法を簡単にまとめます。TOTPは共通の秘密鍵と現在時刻からコードを生成する仕組みで、SMS送信や外部の認証サーバーは不要です。
導入の流れ
- 秘密鍵を生成:ユーザーごとにランダムな秘密鍵を作り、暗号化してサーバーに保存する。
- QRコードで登録:
otpauth://形式のデータをQRコード化し、認証アプリに読み取らせる。 - 登録を確認:アプリに表示された6桁のコードを入力してもらい、正しい場合だけTOTPを有効化する。
- ログイン時に検証:パスワードに加えて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処理・再試行制限などを追加して完成させます。
