Как вы используете bcrypt для хеширования паролей в PHP? - PullRequest
1212 голосов
/ 25 января 2011

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

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

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

Ответы [ 9 ]

1031 голосов
/ 14 июня 2011

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

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

Как использовать 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 <em>(УСТАРЕЛО)

Вы можете использовать функцию 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('$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 PHP Hashing Framework .

286 голосов
/ 12 июня 2013

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

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

Поверьте, криптография сложна.

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

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

Библиотеки

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

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

Начиная с PHP 5.5, вводится новый API для хеширования паролей.Также есть поддерживаемая мной библиотека совместимости shim для 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, '$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: <a href="https://stackoverflow.com/questions/16042128/is-this-a-good-hashing-password-function-in-php-if-not-why/16044003#16044003"> в StackOverflow

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

О BCrypt

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

Завершение

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

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

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

46 голосов
/ 25 января 2011

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

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

35 голосов
/ 25 января 2011

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

PHP - функция шифрования

33 голосов
/ 31 октября 2012

Редактировать: 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

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

27 голосов
/ 11 января 2013

Версия 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.

6 голосов
/ 19 февраля 2014

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

6 голосов
/ 08 декабря 2011

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

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

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

3 голосов
/ 25 марта 2016

Для OAuth 2 пароли:

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