LoginSignup
31
27

More than 5 years have passed since last update.

password_verifyは本当にタイミングセーフなのか?

Last updated at Posted at 2016-03-30

このコードには問題点がある.

<?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'
);
31
27
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
27