620 文字
3 分
PHPでワンタイムパスワードのリプレイ攻撃対策

使用済みのワンタイムパスワードを再利用できないようにするには、データベースやキャッシュに保存する方法が一般的です。以下に、PHPでデータベースとキャッシュを利用した具体的な対策例を紹介します。

データベースを利用する場合

  1. データベースにテーブルを作成し、以下のカラムを定義します。

    • user_id: ユーザーID
    • otp: ワンタイムパスワード
    • timestamp: ワンタイムパスワードの生成時刻
  2. ワンタイムパスワードを生成したら、データベースにレコードを挿入します。

<?php
$userId = 1; // ユーザーID
$otp = generateOTP(); // ワンタイムパスワードを生成
$timestamp = time(); // 現在時刻を取得
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare('INSERT INTO otps (user_id, otp, timestamp) VALUES (:user_id, :otp, :timestamp)');
$stmt->bindParam(':user_id', $userId);
$stmt->bindParam(':otp', $otp);
$stmt->bindParam(':timestamp', $timestamp);
$stmt->execute();
?>
  1. ワンタイムパスワードを検証する前に、データベースにレコードが存在するか確認します。
<?php
$userId = 1; // ユーザーID
$otp = '123456'; // 入力されたワンタイムパスワード
$timestamp = time(); // 現在時刻を取得
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $db->prepare('SELECT * FROM otps WHERE user_id = :user_id AND otp = :otp AND timestamp >= :timestamp - 30'); // 過去30秒以内に生成されたワンタイムパスワードのみ検証
$stmt->bindParam(':user_id', $userId);
$stmt->bindParam(':otp', $otp);
$stmt->bindParam(':timestamp', $timestamp);
$stmt->execute();
$result = $stmt->fetchAll();
if (count($result) > 0) {
// ワンタイムパスワードが有効
echo 'Valid OTP';
// 使用済みのワンタイムパスワードを削除
$stmt = $db->prepare('DELETE FROM otps WHERE user_id = :user_id AND otp = :otp');
$stmt->bindParam(':user_id', $userId);
$stmt->bindParam(':otp', $otp);
$stmt->execute();
} else {
// ワンタイムパスワードが無効
echo 'Invalid OTP';
}
?>

キャッシュを利用する場合

  1. Memcached や Redis などのキャッシュサーバーをインストールします。

  2. ワンタイムパスワードを生成したら、キャッシュに保存します。

<?php
$userId = 1; // ユーザーID
$otp = generateOTP(); // ワンタイムパスワードを生成
$timestamp = time(); // 現在時刻を取得
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$memcached->set($userId . ':' . $otp, $timestamp, 30); // 30秒間キャッシュする
?>
  1. ワンタイムパスワードを検証する前に、キャッシュにレコードが存在するか確認します。
<?php
$userId = 1; // ユーザーID
$otp = '123456'; // 入力されたワンタイムパスワード
$timestamp = time(); // 現在時刻を取得
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$cachedTimestamp = $memcached->get($userId . ':' . $otp);
if ($cachedTimestamp !== false && $cachedTimestamp >= $timestamp - 30) {
// ワンタイムパスワードが有効
echo 'Valid OTP';
// キャッシュからレコードを削除
$memcached->delete($userId . ':' . $otp);
} else {
// ワンタイムパスワードが無効
echo 'Invalid OTP';
}
?>

その他の対策

  • ワンタイムパスワードの長さを十分に長くする (少なくとも 6 桁)
  • ワンタイムパスワードの有効期限を短くする (30 秒など)
  • ワンタイムパスワードを複数回入力できないようにする (例えば、入力画面に残り試行回数カウンターを表示する)

これらの対策を組み合わせることで、より安全な二段階認証システムを構築することができます。