このコードには問題点がある.
<?php
$username = 'ユーザ名';
$password = 'パスワード';
$hashes = [
'mpyw' => '$2y$10$p3v4KlSFqx11/84YF2xTJu4h5hQKhwsH5/X5bqoVgkY./.vFxf3HS',
];
$result = isset($hashes[$username]) && password_verify($password, $hashes[$username]);
password_verify
を実行するかしないかで大きく実行時間に差が現れるため,ユーザ名が存在すること・しないことがバレやすい.これではユーザ名をできるだけ保護したい目的で「ユーザ名またはパスワードのどちらかが違います」のようにメッセージを出したとしても意味が無くなってしまう.
では,ただpassword_verify
をとりあえず実行しておけばいいのか?というとそうでもない模様.
<?php
for ($i = 0; $i < 25; ++$i) {
$hash = '$2y$10$' . str_repeat('A', $i + 1);
$sum = 0.0;
for ($j = 0; $j < 30; ++$j) {
$start = microtime(true);
password_verify('a', $hash);
$end = microtime(true);
$sum += $end - $start;
unset($start, $end);
}
printf("%02d: %.5f\n", $i + 1, $sum / 30);
}
/*
01: 0.00015
02: 0.00013
03: 0.00014
04: 0.00016
05: 0.00020
06: 0.00016
07: 0.00013
08: 0.00014
09: 0.00015
10: 0.00015
11: 0.00013
12: 0.00015
13: 0.00015
14: 0.00016
15: 0.00020
16: 0.00018
17: 0.00015
18: 0.00014
19: 0.00017
20: 0.00013
21: 0.00013
22: 0.08747
23: 0.08111
24: 0.08496
25: 0.08421
*/
ダミーデータを (あり得る文字を使って) 22バイト用意しておけばいい感じに一律になりそうですね.
<?php
$username = 'ユーザ名';
$password = 'パスワード';
$hashes = [
'mpyw' => '$2y$10$p3v4KlSFqx11/84YF2xTJu4h5hQKhwsH5/X5bqoVgkY./.vFxf3HS',
];
$result = password_verify(
$password,
isset($hashes[$username])
? $hashes[$username]
: '$2y$10$abcdefghijklmnopqrstuv'
);