はじめに
password_hash()
と password_verify()
を使えば、PHPで安全なパスワード認証が実現できます。
しかし、現在 PASSWORD_DEFAULT
に指定されている PASSWORD_BCRYPT
には「72バイト制限」という重要な仕様があります。この制限を理解していないと、「異なるパスワードが同じと判定される」という思わぬ落とし穴にハマる可能性があります。
この記事では、PHP8上級試験でも頻出の「BCRYPTの72バイト制限」とその実際の動作を、実行例と図解でわかりやすく解説します。
🔍 実行コード
<?php
declare(strict_types=1);
error_reporting(-1);
$raw_password = str_repeat('a', 72);
var_dump($raw_password);
$hash = password_hash($raw_password, PASSWORD_DEFAULT);
$r = password_verify($raw_password, $hash);
var_dump($r);
$r = password_verify($raw_password . 'abc', $hash);
var_dump($r);
🧪 実行結果
string(72) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
bool(true)
bool(true)
🧠 解説
1️⃣ PASSWORD_DEFAULT の正体
PHP8系(〜8.2)では、PASSWORD_DEFAULT
は PASSWORD_BCRYPT
を指しています。
つまり、このコードは実質的に:
password_hash($raw_password, PASSWORD_BCRYPT);
を呼んでいます。
2️⃣ BCRYPT の「72バイト制限」
BCRYPT では、パスワードの先頭72バイト までしか処理に使用されません。73バイト目以降は自動的に切り捨てられます。
3️⃣ そのため起こる現象
$raw_password
がちょうど72バイトのとき、$raw_password . 'abc'
を渡しても 'abc'
の部分(73〜75バイト目)は無視されるため、実際には 同じ72バイトのデータを比較している ことになります。
🧩 ASCII図解で理解
入力パスワード:
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc"
│<------------------ BCRYPTが使うのはここまで(72バイト) --------------------->│
│ │
└─────────────────────────────┬───────────────────────────────────────────┘
│
▼
BCRYPTはここまでしかハッシュ化に使わない!
4️⃣ 結果の意味
実行内容 | 結果 | 理由 |
---|---|---|
password_verify($raw_password, $hash) | true | 同じ72バイトで一致 |
password_verify($raw_password . 'abc', $hash) | true | 73バイト以降が無視され、一致してしまう |
5️⃣ 試験での正答
この問題に対する模範解答は:
✅ この結果は「正しい(仕様通りの動作)」である。
です。
BCRYPT が72バイト以降を無視するため、仕様上は bool(true), bool(true)
が正しい結果となります。
⚠️ 実務上の注意点
この仕様を知らずに運用すると、以下のような危険があります:
「72文字目以降が違うだけの異なるパスワードが、同じものとして認証されてしまう」
そのため、実務では次のような対策が推奨されます。
✅ 対策例1:パスワード長を制限する
if (strlen($raw_password) > 72) {
throw new Exception('パスワードは72バイト以内で入力してください。');
}
✅ 対策例2:ハッシュ前に短縮(プリハッシュ)
$hashed = password_hash(hash('sha256', $raw_password, true), PASSWORD_DEFAULT);
→ これにより、どんなに長いパスワードでも 32バイトに収まります。
まとめ
項目 | 内容 |
---|---|
PASSWORD_DEFAULT | PHP8ではPASSWORD_BCRYPT |
制限 | 72バイト以降は無視される |
実行結果 | bool(true), bool(true) は仕様通り「正しい」 |
注意点 | 長いパスワードは切り捨てられるため、制限または前処理が必要 |
✅ まとめコメント
password_verify()
の結果が「true,true」になるのは間違いではなく、BCRYPT の仕様通りの動作です。
ただし実務では、72バイトを超えるパスワードの扱いに十分注意しましょう。