По сей день очень многие хранят пароли в базе просто захешировав их с помощью md5 или в лучшем случае SHA-1, что едва ли обеспечивает сколь либо заметную безопасность. У устаревшего и уже признанного ненадёжным md5 существует немало коллизий, а также к нему рассчитаны довольно большие и общедоступные радужные таблицы. Ситуация с SHA-1 лишь немногим лучше.
Использование соли и более стойких алгоритмов хеширование значительно увеличивают надёжность, однако рассчёт даже длинных хешей — не очень сложная задача для современных систем, особенно с тех пор как для рассчётов стали привлекать GPU. Поэтому для увеличения надёжности шифрования паролей следует использовать такой алгоритм, который будет работать настолько медленно, что рассчёт радужных таблиц и атаки методом перебора станут абсолютно неэффективными. Это делает небезызвестная функция bcrypt. Для необратимого шифрования она использует алгоритм blowfish с множеством итераций, и работает очень медленно. Путём изменения количества итераций можно регулировать время, затрачиваемое на шифрования пароля, и найти такую «золотую середину», когда шифрование одного пароля будет достаточно быстрым, а подбор его станет бессмысленным.
В PHP есть встроенная функция crypt(), являющаяся реализацией bcrypt. С версиях PHP 5.3 и выше функция crypt() имеет встроенную реализацию алгоритмов шифрования и больше не зависит от поддержки этих алгоритмов операционной системой, чем и следует воспользоваться. Эта функция поддерживает различные алгоритмы, от DES до SHA-512, и выбирает алгоритм в зависимости от того, какая соль задаётся в качестве её параметра.
Ниже я хочу предложить простейший, состоящий из одной строчки генератор соли. Будучи скормленной функции crypt() данная соль заставит её использовать blowfish с 10 в квадрате итерациями. Это задаётся идентификатором $2a$ (использовать bcrypt), и идущим затем количеством итераций, которые задаются как логарифм по основанию 2 (в данном случае это 210, то есть 1024 итераций. Увеличение параметра на единицу удваивает количество итераций, и следовательно время рассчёта функции.
// Генерируем соль
$salt = '$2a$10$'.substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(),mt_rand()))), 0, 22) . '$';
// Шифруем пароль с применением данной соли
$hashed_password = crypt($password, $salt);
Соль будет длиной 30 байт, из которых 22 полезных и 8 служебных. На моём сервере (Xeon E5520, 2.24 ГГц) рассчёт одного пароля таким способом занимает 0.12 секунд — что очень много. На выходе будет получена строка длиной 60 байт.
Генератор позаимствован из комментариев на странице описания функции на php.net.
Автор: varnav