Безопасный хэш и соль для паролей PHP - PullRequest
1119 голосов
/ 31 декабря 2008

В настоящее время говорят, что MD5 частично небезопасен. Учитывая это, я хотел бы знать, какой механизм использовать для защиты паролем.

Этот вопрос, Является ли «двойное хеширование» паролем менее безопасным, чем одноразовое хеширование? предполагает, что хэширование несколько раз может быть хорошей идеей, тогда как Как реализовать защиту паролем для отдельных файлов? предлагает использовать соль.

Я использую PHP. Я хочу безопасную и быструю систему шифрования паролей. Хэширование пароля в миллион раз может быть безопаснее, но и медленнее. Как добиться хорошего баланса между скоростью и безопасностью? Кроме того, я бы предпочел, чтобы результат имел постоянное количество символов.

  1. Механизм хеширования должен быть доступен на PHP
  2. Должно быть безопасно
  3. Может использоваться соль (в этом случае все соли одинаково хороши? Есть ли способ получить хорошие соли?)

Кроме того, мне следует хранить два поля в базе данных (например, одно с использованием MD5, а другое с использованием SHA)? Будет ли это сделать это безопаснее или небезопаснее?

В случае, если я не был достаточно ясен, я хочу знать, какую функцию (ы) хеширования использовать и как выбрать хорошую соль для обеспечения безопасного и быстрого механизма защиты паролем.

Похожие вопросы, которые не совсем охватывают мой вопрос:

В чем разница между SHA и MD5 в PHP
Простое шифрование пароля
Безопасные методы хранения ключей, паролей для asp.net
Как бы вы внедрили соленые пароли в Tomcat 5.5

Ответы [ 14 ]

943 голосов
/ 31 декабря 2008

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ : Этот ответ был написан в 2008 году.

С тех пор PHP дал нам password_hash и password_verify, и с момента их появления они являются рекомендуемым методом хеширования и проверки пароля.

Теория ответа все еще хорошо читается.

TL; DR

Этикет

  • Не ограничивайте количество символов, которые пользователи могут вводить для паролей. Это делают только идиоты.
  • Не ограничивайте длину пароля. Если ваши пользователи хотят получить предложение с суперкалифрагистическим выражением, не запрещающее им использовать его.
  • Никогда не храните пароль своего пользователя в виде обычного текста.
  • Никогда не посылайте по электронной почте пароль вашему пользователю , за исключением случаев, когда они потеряли свои, и вы отправили временный.
  • Никогда, никогда не регистрируйте пароли каким-либо образом.
  • Никогда не хэшируйте пароли с SHA1 или MD5 или даже SHA256! Современные взломщики могут превышать 60 и 180 миллиардов хэшей в секунду (соответственно).
  • Не смешивайте bcrypt и с raw выводом hash () , либо используйте шестнадцатеричный вывод, либо base64_encode его. (Это относится к любому входу, в котором может содержаться мошенник \0, что может серьезно ослабить безопасность.)

Dos

  • Используйте scrypt, когда можете; bcrypt если не можешь.
  • Используйте PBKDF2, если вы не можете использовать ни bcrypt, ни scrypt, с хешами SHA2.
  • Сбросить все пароли при взломе базы данных.
  • Реализуйте разумную минимальную длину 8-10 символов, плюс требуйте как минимум 1 заглавную букву, 1 строчную букву, число и символ. Это улучшит энтропию пароля, что, в свою очередь, усложнит его взлом. (См. Раздел «Что делает хороший пароль?» Для обсуждения).

В любом случае, зачем хешировать пароли?

Цель хеширования паролей проста: предотвратить злонамеренный доступ к учетным записям пользователей путем взлома базы данных. Таким образом, цель хеширования паролей состоит в том, чтобы удержать хакеров или взломщиков, затратив им слишком много времени или денег для вычисления паролей в виде простого текста. А время / стоимость - лучшие сдерживающие факторы в вашем арсенале.

Еще одна причина, по которой вам нужен надежный и надежный хэш для учетных записей пользователей, - это предоставление вам достаточно времени для изменения всех паролей в системе. Если ваша база данных взломана, вам понадобится достаточно времени, чтобы на минимум заблокировать систему, если не поменять каждый пароль в базе данных.

Джеремия Гроссман, технический директор Whitehat Security, заявил в своем блоге после недавнего восстановления пароля, которое требовало взлома его защиты паролем:

Интересно, что, пережив этот кошмар, я узнал ОЧЕНЬ много, чего не знал о взломе паролей, хранении и сложности. Я понял, почему хранение паролей намного важнее, чем сложность паролей. Если вы не знаете, как хранится ваш пароль, то все, от чего вы действительно можете зависеть - это сложность. Это может быть общеизвестно для паролей и крипто-профессионалов, но для среднего специалиста по InfoSec или веб-безопасности я сильно сомневаюсь .

(Акцент мой.)

Что делает хороший пароль в любом случае?

Энтропия . (Не то чтобы я полностью разделял точку зрения Рэндалла.)

Короче говоря, энтропия - это количество вариаций в пароле. Когда пароль состоит только из строчных латинских букв, это всего 26 символов. Это не большая вариация. Буквенно-цифровые пароли лучше, с 36 символами. Но допускается верхний и нижний регистр, с символами, примерно 96 символов. Это намного лучше, чем просто буквы. Одна проблема состоит в том, чтобы сделать наши пароли запоминающимися, мы вставляем шаблоны, что уменьшает энтропию. Oops!

* 1 089 * Паэнтропия ssword приблизительно легко. Использование полного диапазона символов ascii (примерно 96 набираемых символов) дает энтропию 6,6 на символ, что при 8 символах для пароля все еще слишком мало (52,679 бит энтропии) для будущей безопасности. Но есть и хорошая новость: более длинные пароли и пароли с символами Юникода действительно увеличивают энтропию пароля и усложняют его взлом.

На сайте Crypto StackExchange дольше обсуждается вопрос об энтропии паролей. Хороший поиск в Google также даст много результатов.

В комментариях, которые я говорил с @popnoodles, он отметил, что применение политики паролей длиной X с множеством букв, цифр, символов и т. Д. Может реально уменьшить энтропию, сделав схему паролей более предсказуемы. Я согласен. Randomess, как можно более случайный, всегда является самым безопасным, но наименее запоминающимся решением.

Насколько я могу судить, лучший пароль в мире - Catch-22. Либо это не запоминающееся, слишком предсказуемое, слишком короткое, слишком много символов Юникода (трудно набрать на устройстве Windows / Mobile), слишком длинный и т. Д. Ни один пароль не достаточно хорош для наших целей, поэтому мы должны защищать их, как будто они были в форте Нокс.

Лучшие практики

Bcrypt и scrypt являются текущими лучшими практиками. Scrypt будет лучше, чем bcrypt во времени, но он не воспринимается как стандарт Linux / Unix или веб-серверами и еще не опубликовал подробные обзоры своего алгоритма. Но все же будущее алгоритма выглядит многообещающим. Если вы работаете с Ruby, есть scrypt gem , который поможет вам, и Node.js теперь имеет свой собственный пакет scrypt . Вы можете использовать Scrypt в PHP через расширение Scrypt или расширение Libsodium (оба доступны в PECL).

Я настоятельно рекомендую прочитать документацию для функции crypt , если вы хотите понять, как использовать bcrypt, или найти себе хорошую оболочку или использовать что-то еще как PHPASS для более унаследованной реализации. Я рекомендую минимум 12 раундов bcrypt, если не 15 до 18.

Я передумал об использовании bcrypt, когда узнал, что bcrypt использует только расписание ключевых слов blowfish с механизмом переменных затрат. Последнее позволяет увеличить стоимость взлома пароля путем увеличения и без того дорогого расписания ключей blowfish.

Средние практики

Я почти не могу представить эту ситуацию больше. PHPASS поддерживает PHP 3.0.18–5.3, поэтому его можно использовать практически во всех возможных установках, и его следует использовать, если вы точно не знаете , что ваша среда поддерживает bcrypt.

Но предположим, что вы вообще не можете использовать bcrypt или PHPASS. Что тогда?

Попробуйте реализовать PDKBF2 с максимальным количеством раундов , которое может выдержать ваша среда / приложение / восприятие пользователя. Наименьшее число, которое я бы порекомендовал, это 2500 раундов. Кроме того, обязательно используйте hash_hmac () , если это возможно, чтобы сделать операцию более трудной для воспроизведения.

Будущие практики

В PHP 5.5 появилась библиотека для полной защиты паролем , которая избавляет от любых трудностей при работе с bcrypt. Хотя большинство из нас придерживаются PHP 5.2 и 5.3 в большинстве распространенных сред, особенно на общих хостах, @ircmaxell создал уровень совместимости для предстоящего API, обратно совместимого с PHP 5.3.7.

Криптография, обзор и отказ от ответственности

Вычислительная мощность, необходимая для взлома хешированного пароля, не существует. Единственный способ «взломать» пароль для компьютеров - это воссоздать его и смоделировать алгоритм хеширования, используемый для его защиты. Скорость хэша линейно связана с его способностью к грубому принуждению. Хуже того, большинство алгоритмов хеширования можно легко распараллелить, чтобы они работали еще быстрее. Вот почему такие дорогостоящие схемы, как bcrypt и scrypt, так важны.

Вы не можете предвидеть все угрозы или пути атаки, поэтому вы должны приложить все усилия, чтобы защитить своих пользователей заранее . Если вы этого не сделаете, то вы можете даже упустить тот факт, что на вас напали, пока не стало слишком поздно ... и вы несете ответственность . Чтобы избежать этой ситуации, начните действовать параноиком. Атакуйте свое собственное программное обеспечение (внутренне) и пытайтесь украсть учетные данные пользователя или изменить учетные записи других пользователей или получить доступ к их данным. Если вы не проверяете безопасность своей системы, вы не можете винить никого, кроме себя.

Наконец: я не криптограф. Что бы я ни говорил, это моё мнение, но мне кажется, что оно основано на здравом смысле ... и много читаю. Помните, будьте настолько параноиком, насколько это возможно, делайте вещи настолько сложными, насколько это возможно, а затем, если вы все еще беспокоитесь, свяжитесь с хакером или криптографом в белой шляпе, чтобы узнать, что они говорят о вашем коде / системе.

129 голосов
/ 08 ноября 2011

Гораздо более короткий и безопасный ответ - вообще не пишите свой собственный механизм паролей , используйте проверенный и проверенный механизм.

  • PHP 5.5 или выше: password_hash () - хорошее качество и часть ядра PHP.
  • Старые версии PHP: библиотека OpenWall phpass намного лучше, чем большинство пользовательских кодов - используется в WordPress, Drupal и т. Д.

Большинство программистов просто не имеют опыта для безопасного написания кода, связанного с шифрованием, без внедрения уязвимостей.

Быстрая самопроверка: что такое растяжение пароля и сколько итераций следует использовать? Если вы не знаете ответа, вам следует использовать password_hash(), поскольку расширение пароля теперь является критически важной функцией механизмов паролей из-за гораздо более быстрых процессоров и использования графических процессоров и ПЛИС для взлома паролей со скоростью миллиардов попыток в секунду (с графическими процессорами).

Например, вы можете взломать все 8-символьные пароли Windows за 6 часов , используя 25 графических процессоров, установленных на 5 настольных ПК. Это перебор, т. Е. Перечисление и проверка каждого 8-символьного пароля Windows , включая специальные символы, и не является атакой по словарю. Это было в 2012 году, по состоянию на 2018 год вы могли использовать меньше графических процессоров или быстрее взломать 25 графических процессоров.

Существует также множество радужных атак на пароли Windows, которые выполняются на обычных процессорах и очень быстры. Все это потому, что Windows все еще не засоляет и не растягивает своих паролей, даже в Windows 10 - не совершайте ту же ошибку, что и Microsoft!

См. Также:

  • отличный ответ с подробностями о том, почему password_hash() или phpass - лучший путь.
  • хорошая статья в блоге с указанием рекомендуемых «коэффициентов работы» (количество итераций) для основных алгоритмов, включая bcrypt, scrypt и PBKDF2.
41 голосов
/ 31 декабря 2008

Я бы не стал хранить хэшированные пароли двумя разными способами, потому что тогда система, по крайней мере, столь же слабая, как и самый слабый из используемых алгоритмов хеширования.

34 голосов
/ 22 сентября 2014

Начиная с PHP 5.5, PHP имеет простые, безопасные функции для хеширования и проверки паролей, password_hash () и password_verify ()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

Когда используется password_hash(), он генерирует случайную соль и включает ее в выводимый хэш (вместе с затратами и используемым алгоритмом.) password_verify() затем считывает этот хэш и определяет используемый метод соли и шифрования, и проверяет его по предоставленному незашифрованному паролю.

Предоставление PASSWORD_DEFAULT указывает PHP на использование алгоритма хеширования по умолчанию установленной версии PHP. Какой именно этот алгоритм предназначен для изменения со временем в будущих версиях, так что он всегда будет одним из самых сильных доступных алгоритмов.

Увеличение стоимости (по умолчанию 10) усложняет хэш-обработку хэша, но также означает, что создание хешей и проверка паролей против них будут более трудоемкими для ЦП вашего сервера.

Обратите внимание, что даже если алгоритм хеширования по умолчанию может измениться, старые хэши будут продолжать проверяться очень хорошо, потому что используемый алгоритм хранится в хэше и password_verify() получает его.

31 голосов
/ 12 февраля 2010

Хотя на вопрос был дан ответ, я просто хочу повторить, что соли, используемые для хеширования, должны быть случайными и не похожими на адреса электронной почты, как было предложено в первом ответе.

Более подробное объяснение доступно по адресу - http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/

Недавно у меня была дискуссия о том, хэши паролей засолены случайным образом биты более безопасны, чем соленые с угаданными или известными соли. Давайте посмотрим: если система хранения пароля скомпрометирована как а также система, которая хранит случайную соль, злоумышленник иметь доступ к хешу, а также соли, так что соль случайная или нет, не имеет значения. Злоумышленник может сгенерировать предварительно вычисленный Радужные столы, чтобы взломать хэш. Здесь начинается интересная часть не так тривиально генерировать предварительно вычисленные таблицы. Давайте возьмем пример модели безопасности WPA. Ваш пароль WPA фактически никогда не отправляется Беспроводная точка доступа. Вместо этого он хэшируется с вашим SSID ( сетевое имя - как Linksys, Dlink и т. д.). Очень хорошее объяснение того, как это работает здесь. Чтобы восстановить пароль из хэша, вы будете нужно знать как пароль, так и соль (имя сети). Церковь Wi-Fi уже имеет предварительно вычисленные хеш-таблицы, которые имеют 1000 лучших SSID и около 1 миллиона паролей. Размер всех таблиц составляет около 40 ГБ. Как вы можете прочитать на их сайте, кто-то использовал 15 массивов FGPA за 3 дня генерировать эти таблицы. Предполагая, что жертва использует SSID как «A387csf3 ″ и пароль как« 123456 », будут ли они взломаны таблицы? Нет! .. это не может. Даже если пароль слабый, таблицы У меня нет хэшей для SSID a387csf3. Это красота наличия случайная соль. Это будет сдерживать взломщиков, которые процветают на предварительно вычисленных столы. Может ли это остановить решительного хакера? Возможно нет. Но используя случайные соли обеспечивают дополнительный слой защиты. Пока мы на В этой теме давайте обсудим дополнительное преимущество хранения случайных соли по отдельной системе. Сценарий № 1: хеши паролей хранятся в системе X и значения соли, используемые для хеширования, хранятся в системе Y. Эти значения соли являются предположительными или известными (например, имя пользователя) Сценарий № 2: Хеши паролей хранятся в системе X, а значения соли используются для хеширование хранится в системе Y. Эти значения соли являются случайными. В случае Система X была скомпрометирована, как вы можете догадаться, существует огромный Преимущество использования случайной соли в отдельной системе (сценарий № 2). Атакующий должен будет угадать дополнительные значения, чтобы иметь возможность взломать хэши. Если используется 32-битная соль, 2 ^ 32 = 4 294 967 296 (около 4,2 миллиард) итераций может потребоваться для каждого угаданного пароля.

26 голосов
/ 28 апреля 2013

Я просто хочу отметить, что PHP 5.5 включает API хеширования паролей , который обеспечивает обертку вокруг crypt(). Этот API-интерфейс значительно упрощает задачу хеширования, проверки и перефразирования хэшей паролей. Автор также выпустил пакет совместимости (в виде одного файла password.php, который вы просто используете require) для тех, кто использует PHP 5.3.7 и более поздние версии и хочет использовать это право Теперь.

Пока он поддерживает только BCRYPT, но его легко расширить, чтобы включить другие методы хэширования паролей, и поскольку техника и стоимость хранятся как часть хэша, изменения в предпочтительной технике / стоимости хэширования не сделают недействительными текущие хэши. структура будет автоматически использовать правильную технику / стоимость при проверке. Он также обрабатывает создание «безопасной» соли, если вы явно не определяете свою собственную.

API предоставляет четыре функции:

  • password_get_info() - возвращает информацию о данном хеше
  • password_hash() - создает хэш пароля
  • password_needs_rehash() - проверяет, соответствует ли данный хеш заданным параметрам. Полезно, чтобы проверить, соответствует ли хэш вашей текущей схеме техники / стоимости, что позволяет при необходимости перефразировать
  • password_verify() - проверяет, соответствует ли пароль хешу

В настоящее время эти функции принимают константы паролей PASSWORD_BCRYPT и PASSWORD_DEFAULT, которые на данный момент являются синонимами, с той разницей, что PASSWORD_DEFAULT "может измениться в новых версиях PHP, когда поддерживаются более новые, более сильные алгоритмы хеширования". Использование PASSWORD_DEFAULT и password_needs_rehash () при входе в систему (и, при необходимости, перефразировке) должно гарантировать, что ваши хэши достаточно устойчивы к атакам методом перебора и практически не работают для вас.

РЕДАКТИРОВАТЬ: Я только что понял, что это кратко упоминается в ответе Роберта К. Я оставлю этот ответ здесь, так как думаю, что он дает немного больше информации о том, как он работает, и о простоте его использования для тех, кто не знает безопасности.

18 голосов
/ 26 июня 2012

Я использую Phpass , который представляет собой простой однофайловый PHP-класс, который можно очень легко реализовать практически в каждом PHP-проекте. Смотри также H .

По умолчанию используется самое мощное из доступных шифрований, которое реализовано в Phpass (bcrypt) и использует другие алгоритмы шифрования вплоть до MD5, чтобы обеспечить обратную совместимость с такими платформами, как Wordpress.

Возвращенный хеш может быть сохранен в базе данных как есть. Пример использования для генерации хэша:

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

Для подтверждения пароля можно использовать:

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);
13 голосов
/ 09 апреля 2014

ПОМНИТЕ

Много было сказано о шифровании паролей для PHP, большинство из которых - очень полезный совет, но прежде чем вы даже начнете процесс использования PHP для шифрования паролей, убедитесь, что у вас реализовано следующее или готово к реализации.

SERVER

ПОРТЫ

Независимо от того, насколько хорошо ваше шифрование, если вы не обеспечите надлежащую защиту сервера, на котором работает PHP и БД, все ваши усилия бесполезны. Большинство серверов работают относительно одинаково, им назначены порты, позволяющие вам получить к ним удаленный доступ либо через ftp, либо через оболочку. Убедитесь, что вы изменили порт по умолчанию для любого удаленного подключения, которое у вас активно. Не делая этого, вы фактически заставили злоумышленника сделать на один шаг меньше доступа к вашей системе.

USERNAME

Для всего хорошего в мире не используйте имя пользователя admin, root или что-то подобное. Также, если вы работаете в системе на основе Unix, НЕ делайте доступной учетную запись root, это всегда должно быть только sudo.

ПАРОЛЬ

Вы говорите своим пользователям, чтобы они создавали хорошие пароли, чтобы избежать взлома, делайте то же самое. Какой смысл проходить через все усилия по запиранию входной двери, когда задняя дверь широко открыта.

DATABASE

SERVER

В идеале вы хотите, чтобы ваша БД и ПРИЛОЖЕНИЕ были на отдельных серверах. Это не всегда возможно из-за стоимости, но это обеспечивает некоторую безопасность, так как злоумышленнику придется пройти два шага, чтобы получить полный доступ к системе.

USER

У вашего приложения всегда должна быть своя собственная учетная запись для доступа к БД, и только дайте ему необходимые привилегии.

Тогда создайте для вас отдельную учетную запись пользователя, которая нигде не хранится на сервере, даже в приложении.

Как всегда, НЕ делайте этот корень или что-то подобное.

ПАРОЛЬ

Следуйте тем же правилам, что и для всех надежных паролей. Также не используйте один и тот же пароль для любых учетных записей SERVER или DB в той же системе.

PHP

ПАРОЛЬ

НИКОГДА не храните пароль в вашей БД, вместо этого храните хеш и уникальную соль, я объясню почему позже.

HASHING

ОДИН СПОСОБ ХЕШИНГА !!!!!!!, Никогда не хэшируйте пароль таким образом, чтобы его можно было перевернуть, Хэши должны быть односторонними, то есть вы не можете изменить их и сравнить их с паролем, вместо этого вы хэшируете введенный пароль аналогичным образом и сравните два хеша. Это означает, что даже если злоумышленник получит доступ к БД, он не знает, что на самом деле представляет собой пароль, а только его результирующий хеш. Что означает больше безопасности для ваших пользователей в худшем из возможных сценариев.

Существует множество хороших хеш-функций (password_hash, hash и т. Д.), Но вам нужно выбрать хороший алгоритм, чтобы хеш был эффективным. (bcrypt и подобные ему являются достойными алгоритмами.)

Когда ключом является скорость хэширования, чем медленнее, тем более устойчивы к атакам грубой силы.

Одна из наиболее распространенных ошибок в хешировании заключается в том, что хеши не являются уникальными для пользователей. Это происходит главным образом потому, что соли не генерируются однозначно.

соления

Пароли всегда должны быть засолены перед хэшированием. Соление добавляет к паролю случайную строку, поэтому похожие пароли в БД не выглядят одинаково. Однако, если соль не уникальна для каждого пользователя (например, вы используете жестко закодированную соль), то вы в значительной степени сделали свою соль бесполезной. Потому что, как только злоумышленник узнает одну соль пароля, он найдет соль для всех них.

Когда вы создаете соль, убедитесь, что она уникальна для пароля, который она солит, а затем сохраните в вашей БД заполненный хеш и соль. Это сделает так, чтобы злоумышленник должен был по отдельности взломать каждую соль и хеш, прежде чем они смогут получить доступ. Это означает, что для злоумышленника нужно гораздо больше времени и работы.

ПОЛЬЗОВАТЕЛИ, СОЗДАЮЩИЕ ПАРОЛИ

Если пользователь создает пароль через интерфейс, это означает, что его нужно отправить на сервер. Это открывает проблему безопасности, потому что это означает, что незашифрованный пароль отправляется на сервер, и если злоумышленник может прослушать и получить доступ, что вся ваша безопасность в PHP бесполезна. ВСЕГДА передайте данные БЕЗОПАСНО, это делается через SSL, но будьте утомлены, даже если SSL не безупречен (например, недостаток OpenSSL Heartbleed).

Также заставьте пользователя создать безопасный пароль, это просто и всегда должно быть сделано, пользователь будет благодарен за это в конце.

Наконец, независимо от того, какие меры безопасности вы принимаете, ничто не является на 100% безопасным, чем более продвинутой становится технология защиты, тем более совершенными становятся атаки. Но выполнение этих шагов сделает ваш сайт более безопасным и намного менее желательным для атакующих.

Вот класс PHP, который легко создает хеш и соль для пароля

http://git.io/mSJqpw

12 голосов
/ 31 декабря 2008

Google сообщает, что SHA256 доступен для PHP.

Вы обязательно должны использовать соль. Я бы порекомендовал использовать случайные байты (и не ограничиваться символами и цифрами). Как обычно, чем дольше вы выбираете, тем безопаснее, тем медленнее становится. 64 байта должно быть хорошо, я думаю.

8 голосов
/ 19 сентября 2013

Я нашел идеальную тему по этому вопросу здесь: https://crackstation.net/hashing-security.htm, Я хотел, чтобы вы получили от этого выгоду, вот еще и исходный код, обеспечивающий защиту от атак на основе времени.

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>
...