🐘
PHPでワンタイムパスワードのリプレイ攻撃対策
使用済みのワンタイムパスワードを再利用できないようにするには、データベースやキャッシュに保存する方法が一般的です。以下に、PHPでデータベースとキャッシュを利用した具体的な対策例を紹介します。
データベースを利用する場合
-
データベースにテーブルを作成し、以下のカラムを定義します。
-
user_id
: ユーザーID -
otp
: ワンタイムパスワード -
timestamp
: ワンタイムパスワードの生成時刻
-
-
ワンタイムパスワードを生成したら、データベースにレコードを挿入します。
<?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();
?>
- ワンタイムパスワードを検証する前に、データベースにレコードが存在するか確認します。
<?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';
}
?>
キャッシュを利用する場合
-
Memcached や Redis などのキャッシュサーバーをインストールします。
-
ワンタイムパスワードを生成したら、キャッシュに保存します。
<?php
$userId = 1; // ユーザーID
$otp = generateOTP(); // ワンタイムパスワードを生成
$timestamp = time(); // 現在時刻を取得
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);
$memcached->set($userId . ':' . $otp, $timestamp, 30); // 30秒間キャッシュする
?>
- ワンタイムパスワードを検証する前に、キャッシュにレコードが存在するか確認します。
<?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 秒など)
- ワンタイムパスワードを複数回入力できないようにする (例えば、入力画面に残り試行回数カウンターを表示する)
これらの対策を組み合わせることで、より安全な二段階認証システムを構築することができます。
Discussion