/Код-ревью
9 января 2026
Небезопасный кеш данных
Кеширование данных – штука полезная для скорости работы и оптимизации. Но часто кеш преподносит сюрпризы, если с ним напортачить или что-то не предусмотреть.523
682
Всем известно, что пароли ни в коем случае нельзя хранить в открытом виде. Перед сохранением в БД или куда-либо еще данные хешируются алгоритмами, которые работают только в одну сторону – можно создать хеш из данных, но обратно получить оригинал не получится. Расшифровать хеш нельзя, можно только подобрать перебором. Иногда хеши могут даже совпадать на разных исходных данных, но в любом случае время на «взлом» хеша требуется очень большое, и это не гарантирует получение оригинальных данных.
Для хеширования существует много разных алгоритмов, и каждый имеет свои особенности, длину и структуру результата. Самые популярные алгоритмы – это SHA-256, SHA-1, MD5 и Bcrypt. За годы разработки я сталкивался со многими, но с Bcrypt почему-то дел особо не имел, хотя он используется во многих языках программирования, в том числе и в PHP. И на одном из CTF судьба свела с этим алгоритмом – это было весьма полезно, особенно в связке с PHP. Одна особенность может создать проблемы в безопасности.
По задачке была форма и код части бэкенда авторизации. Приведу только самую интересную часть, ее будет достаточно для понимания:
<?php
// соль
$entropy = "additional-entropy-for-super-secure-passwords-you-will-never-guess";
$login = 'admin';
// пароль не известен, он в недоступном конфиге
$adminPass = getEnv('admin_password');
// настоящий хеш
$hashAuth = password_hash($username . $entropy . $adminPass, PASSWORD_BCRYPT);
// проверка данных из формы авторизации на соответствие хешу
if(password_verify(($_POST['user'] . $entropy . $_POST['pass']), $hashAuth)){
// успешная авторизация
}
Известен хеш, соль пароля и логин админа. Сам пароль недоступен. Нужно пройти авторизацию.
Если посмотреть документацию PHP по методу password_hash, который использует Bcrypt для хеширования, то там есть предупреждение: использование алгоритма PASSWORD_BCRYPT приведёт к обрезанию поля password до максимальной длины – 72 байта. То есть для создания хеша используется только начало строки, если она больше 72 символов.
После этого предупреждения уязвимость сразу становится понятной – хеш формируется на основе «склеенных» в строку логина, соли и пароля. Но соль здесь длинная – 66 символов, логин – 5 символов, это уже 71 байт. Под сам пароль остается всего один символ. Все, что остается – проверить 95 вариантов (печатные ASCII-коды от 32 до 126) и подобрать первый символ пароля. Можно даже руками все варианты перебрать.
<?php
$hash = '$2y$10$Jkl13nDRnEiEe2t.NiUzaeZsyuTCFXn8Nmsfho4TixdEmFFfJITDq';
// Длина соли 66 символоы
$entropy = "additional-entropy-for-super-secure-passwords-you-will-never-guess";
// логин длинною 5 символов
$login = 'admin';
// длина хешируемой части строки - 72,
// но соль + логин уже заняли 71 символ
// оба пароля будут валидные,
// т.к используется лишь первый символ - a
$realPassword = 'abcdef';
$fakePassword = 'a111222';
$hashAuth = password_hash($login . $entropy . $realPassword, PASSWORD_BCRYPT);
var_dump(password_verify(($login . $entropy . $realPassword), $hashAuth));
var_dump(password_verify(($login . $entropy . $fakePassword), $hashAuth));
Код с выводом результатов и хешей можно запустить онлайн тут: ссылка.