Как вы используете bcrypt для хеширования паролей в PHP?

Время от времени я слышу совет «Использовать bcrypt для хранения паролей в PHP, правила bcrypt».

Но что такое bcrypt ? PHP не предлагает таких функций, Wikipedia болтает о утилите шифрования файлов и веб-поиске, просто раскрывает несколько реализаций Blowfish на разных языках. Теперь Blowfish также доступен на PHP через mcrypt , но как это помогает при хранении паролей? Blowfish – это шифр общего назначения, он работает двумя способами. Если он может быть зашифрован, его можно расшифровать. Пароли нуждаются в односторонней хэш-функции.

Какое объяснение?

Related of "Как вы используете bcrypt для хеширования паролей в PHP?"

bcrypt – это алгоритм хэширования, который масштабируется с помощью аппаратного обеспечения (через настраиваемое количество раундов). Его медленность и несколько раундов гарантирует, что злоумышленник должен развернуть огромные средства и оборудование, чтобы взломать ваши пароли. Добавьте к этим солям по паролю ( bcrypt REQUIRES salt), и вы можете быть уверены, что атака практически невозможна без какой-либо смехотворной суммы средств или оборудования.

bcrypt использует алгоритм Eksblowfish для хэш-паролей. Хотя фаза шифрования Eksblowfish и Blowfish абсолютно одинакова, ключевая фаза расписания Eksblowfish гарантирует, что любое последующее состояние зависит как от соли, так и от ключа (пароль пользователя), и никакое состояние не может быть предварительно вычислено без знания обоим. Из-за этого ключевого отличия, bcrypt является односторонним алгоритмом хэширования. Вы не можете получить пароль обычного текста, не зная соли, раундов и ключа (пароль). [ Источник ]

Как использовать bcrypt:

Использование PHP> = 5.5-DEV

Теперь функции хеширования пароля теперь встроены непосредственно в PHP> = 5.5 . Теперь вы можете использовать password_hash() для создания хэша bcrypt любого пароля:

 <?php // Usage 1: echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n"; // $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // For example: // $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a // Usage 2: $options = [ 'cost' => 11 ]; echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n"; // $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C 

Чтобы проверить пароль, предоставленный пользователем для существующего хеша, вы можете использовать password_verify() как таковой:

 <?php // See the password_hash() example to see where this came from. $hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq'; if (password_verify('rasmuslerdorf', $hash)) { echo 'Password is valid!'; } else { echo 'Invalid password.'; } 

Используя PHP> = 5.3.7, <5.5-DEV (также RedHat PHP> = 5.3.3)

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

Использование PHP <5.3.7 (DEPRECATED)

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

 class Bcrypt { private $rounds; public function __construct($rounds = 12) { if (CRYPT_BLOWFISH != 1) { throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt"); } $this->rounds = $rounds; } public function hash($input) { $hash = crypt($input, $this->getSalt()); if (strlen($hash) > 13) return $hash; return false; } public function verify($input, $existingHash) { $hash = crypt($input, $existingHash); return $hash === $existingHash; } private function getSalt() { $salt = sprintf('$2a$%02d$', $this->rounds); $bytes = $this->getRandomBytes(16); $salt .= $this->encodeBytes($bytes); return $salt; } private $randomState; private function getRandomBytes($count) { $bytes = ''; if (function_exists('openssl_random_pseudo_bytes') && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL is slow on Windows $bytes = openssl_random_pseudo_bytes($count); } if ($bytes === '' && is_readable('/dev/urandom') && ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) { $bytes = fread($hRand, $count); fclose($hRand); } if (strlen($bytes) < $count) { $bytes = ''; if ($this->randomState === null) { $this->randomState = microtime(); if (function_exists('getmypid')) { $this->randomState .= getmypid(); } } for ($i = 0; $i < $count; $i += 16) { $this->randomState = md5(microtime() . $this->randomState); if (PHP_VERSION >= '5') { $bytes .= md5($this->randomState, true); } else { $bytes .= pack('H*', md5($this->randomState)); } } $bytes = substr($bytes, 0, $count); } return $bytes; } private function encodeBytes($input) { // The following is code from the PHP Password Hashing Framework $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $output = ''; $i = 0; do { $c1 = ord($input[$i++]); $output .= $itoa64[$c1 >> 2]; $c1 = ($c1 & 0x03) << 4; if ($i >= 16) { $output .= $itoa64[$c1]; break; } $c2 = ord($input[$i++]); $c1 |= $c2 >> 4; $output .= $itoa64[$c1]; $c1 = ($c2 & 0x0f) << 2; $c2 = ord($input[$i++]); $c1 |= $c2 >> 6; $output .= $itoa64[$c1]; $output .= $itoa64[$c2 & 0x3f]; } while (true); return $output; } } 

Вы можете использовать этот код следующим образом:

 $bcrypt = new Bcrypt(15); $hash = $bcrypt->hash('password'); $isGood = $bcrypt->verify('password', $hash); 

Кроме того, вы также можете использовать Portable Hash Framework .

Итак, вы хотите использовать bcrypt? Потрясающие! Однако, как и в других областях криптографии, вы не должны делать это самостоятельно. Если вам нужно беспокоиться о чем-либо вроде управления ключами или хранении солей или генерации случайных чисел, вы делаете это неправильно.

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

Face It, криптография сложна.

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

Вместо этого просто используйте библиотеку. Некоторые из них существуют в зависимости от ваших требований.

Библиотеки

Вот разбивка некоторых из наиболее распространенных API.

PHP 5.5 API – (доступно для 5.3.7+)

Начиная с PHP 5.5, вводится новый API для хеширования паролей. Существует также библиотека совместимости прокладок, которая поддерживается мной (5.3). Это имеет преимущество быть рецензируемой и простой в использовании реализации.

 function register($username, $password) { $hash = password_hash($password, PASSWORD_BCRYPT); save($username, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); if (password_verify($password, $hash)) { //login } else { // failure } } 

На самом деле, это было очень просто.

Ресурсы:

  • Документация: на PHP.net
  • Библиотека совместимости: на GitHub
  • PHP RFC: на wiki.php.net

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Это еще один API, похожий на PHP 5.5, и делает аналогичную цель.

 function register($username, $password) { $bcrypt = new Zend\Crypt\Password\Bcrypt(); $hash = $bcrypt->create($password); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $bcrypt = new Zend\Crypt\Password\Bcrypt(); if ($bcrypt->verify($password, $hash)) { //login } else { // failure } } 

Ресурсы:

  • Документация: по Zend
  • Сообщение блога: Хеширование пароля с помощью Zend Crypt

PasswordLib

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

 function register($username, $password) { $lib = new PasswordLib\PasswordLib(); $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12)); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $lib = new PasswordLib\PasswordLib(); if ($lib->verifyPasswordHash($password, $hash)) { //login } else { // failure } } 

Рекомендации:

  • Исходный код / ​​документация: GitHub

PHPASS

Это слой, который поддерживает bcrypt, но также поддерживает довольно сильный алгоритм, который полезен, если у вас нет доступа к PHP> = 5.3.2 … Он фактически поддерживает PHP 3.0+ (хотя и не с bcrypt).

 function register($username, $password) { $phpass = new PasswordHash(12, false); $hash = $phpass->HashPassword($password); save($user, $hash); } function login($username, $password) { $hash = loadHashByUsername($username); $phpass = new PasswordHash(12, false); if ($phpass->CheckPassword($password, $hash)) { //login } else { // failure } } 

Ресурсы

  • Код: cvsweb
  • Сайт проекта: на OpenWall
  • Обзор алгоритма <5.3.0: на StackOverflow

Примечание. Не используйте альтернативы PHPASS, которые не размещены в openwall, это разные проекты !!!

О BCrypt

Если вы заметили, каждая из этих библиотек возвращает одну строку. Это из-за того, как BCrypt работает внутри страны. И есть TON ответов об этом. Вот выбор, который я написал, что я не буду копировать / вставлять сюда, но ссылку на:

  • Фундаментальная разница между алгоритмами шифрования и шифрования – объяснение терминологии и некоторой базовой информации о них.
  • О реверсировании хэшей без радужных столов – По сути, мы должны использовать bcrypt в первую очередь …
  • Хранение bcrypt Хэши – в основном, почему соль и алгоритм включены в хэш-результат.
  • Как обновить стоимость хэшей bcrypt – в основном, как выбрать, а затем сохранить стоимость хэша bcrypt.
  • Как хэш длинные пароли с помощью bcrypt – объяснение 72-символьного пароля для пароля bcrypt.
  • Как bcrypt использует соли
  • Лучшие практики использования соляных и перечных паролей. В принципе, не используйте «перец»,
  • Миграция старых паролей md5 на bcrypt

Заворачивать

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

Опять же, если вы используете crypt() напрямую, вы, вероятно, делаете что-то неправильно. Если ваш код использует hash() (или md5() или sha1() ), вы почти наверняка делаете что-то неправильно.

Просто используйте библиотеку …

Вы получите много информации в Enough With The Rainbow Tables: что вам нужно знать о безопасных схемах паролей или переносимой хеширования PHP .

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

Вы можете создать односторонний хеш с помощью bcrypt с помощью функции crypt() PHP и передать соответствующую соль Blowfish. Наиболее важным из всего уравнения является то, что A) алгоритм не был скомпрометирован и B) вы должным образом соляли каждый пароль . Не используйте соль для всей заявки; который открывает все ваше приложение для атаки из одного набора таблиц Rainbow.

PHP – функция склепа


Изменить: 2013.01.15. Если ваш сервер будет поддерживать его, используйте вместо этого решение martinstoeckli .


Все хотят сделать это более сложным, чем оно есть. Функция crypt () выполняет большую часть работы.

 function blowfishCrypt($password,$cost) { $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $salt=sprintf('$2y$%02d$',$cost); //For PHP < PHP 5.3.7 use this instead // $salt=sprintf('$2a$%02d$',$cost); //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand mt_srand(); for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)]; return crypt($password,$salt); } 

Пример:

 $hash=blowfishCrypt('password',10); //This creates the hash $hash=blowfishCrypt('password',12); //This creates a more secure hash if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password 

Я знаю, что это должно быть очевидно, но, пожалуйста, не используйте «пароль» в качестве пароля.

Версия 5.5 PHP будет иметь встроенную поддержку BCrypt, функции password_hash() и password_verify() . На самом деле это всего лишь обертки вокруг функции crypt() и облегчат ее правильное использование. Он заботится о создании безопасной случайной соли и обеспечивает хорошие значения по умолчанию.

Самый простой способ использовать эти функции:

 $hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT); $isPasswordCorrect = password_verify($password, $existingHashFromDb); 

Этот код будет хэш-пароль с помощью BCrypt (алгоритм 2y ), генерирует случайную соль из случайного источника ОС и использует параметр стоимости по умолчанию (на данный момент это 10). Вторая строка проверяет, совпадает ли введенный пользователем пароль с уже сохраненным хэш-значением.

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

 $hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11)); 

В отличие от параметра "cost" , лучше опустить параметр "salt" , потому что функция уже делает все возможное, чтобы создать криптографически безопасную соль.

Для PHP версии 5.3.7 и более поздних версий существует пакет совместимости от того же автора, который сделал функцию password_hash() . Для версий PHP до 5.3.7 нет поддержки для crypt() с 2y , безопасным алгоритмом BCrypt для unicode. Вместо него можно заменить 2a , что является лучшей альтернативой для ранних версий PHP.

Альтернативой является использование scrypt, специально разработанного для того, чтобы превосходить Bcrypt Колина Персиваля в его статье . В PECL есть расширение PHP с расширением scrypt . В идеале этот алгоритм будет переведен в PHP, чтобы его можно было указать для функций password_ * (в идеале, как «PASSWORD_SCRYPT»), но этого пока нет.

Текущее мышление: хеши должны быть самыми медленными, а не самыми быстрыми. Это подавляет атаки радужного стола .

Также связано, но предосторожно: злоумышленник никогда не должен иметь неограниченный доступ к вашему экрану входа в систему. Чтобы предотвратить это: настройте таблицу отслеживания IP-адреса, которая записывает каждое нажатие вместе с URI. Если более 5 попыток входа в систему поступают с одного и того же IP-адреса в любой пятиминутный период, заблокируйте с объяснением. Вторичный подход состоит в том, чтобы иметь двухуровневую схему паролей, например, банки. Помещение блокировки для сбоев на втором проходе повышает безопасность.

Резюме: замедлить атакующего, используя длительные хэш-функции. Кроме того, заблокируйте слишком много обращений к вашему логину и добавьте второй уровень пароля.

Для паролей OAuth 2 :

 $bcrypt = new \Zend\Crypt\Password\Bcrypt; $bcrypt->create("youpasswordhere", 10)