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

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

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

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

9 ответов


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

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

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

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

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

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// y$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// y$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// yDP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

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

<?php
// See the password_hash() example to see where this came from.
$hash = 'y$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

использование PHP > = 5.3.7, = 5.3.3)

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

использование PHP (рекомендуется)

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

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('a$%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);

кроме того, вы также можете использовать портативный PHP хеширования Framework.


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

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

посмотрите правде в глаза, криптография трудна.

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

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

библиотеки

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

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

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

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
    }
}

действительно, это должно быть очень просто.

ресурсы:

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
    }
}

ресурсы:

PasswordLib

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

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, 'y$', 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
  • обзор алгоритма на StackOverflow

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

О BCrypt

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

Завернуть

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

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

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


вы получите много информации в Достаточно Радужных Таблиц: Что Вам Нужно Знать О Безопасных Схемах Паролей или портативный PHP пароль хэширования framework.

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


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

функция PHP-Crypt



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


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

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('a$%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 и более поздних версий существует пакет обеспечения совместимости, от того же автора, что сделал


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


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

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

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


на OAuth 2 пароль:

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