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


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

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

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


1137


источник


Ответы:


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

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)

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

Использование PHP <5.3.7 (Устаревшее)

Вы можете использовать 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);

Кроме того, вы также можете использовать Портативная PHP Hashing Framework ,


971



Итак, вы хотите использовать 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
    }
}

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

Ресурсы:

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

Ресурсы

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

О BCrypt

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

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

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

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

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


273



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

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


43



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

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


34




Изменить: 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

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


32



Версия 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 в юникоде. Вместо этого можно заменить его 2a, что является лучшей альтернативой более ранним версиям PHP.


24



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


5



Current thinking: hashes should be the slowest available, not the fastest possible. This suppresses rainbow table attacks.

Also related, but precautionary: An attacker should never have unlimited access to your login screen. To prevent that: Set up an IP address tracking table that records every hit along with the URI. If more than 5 attempts to login come from the same IP address in any five minute period, block with explanation. A secondary approach is to have a two-tiered password scheme, like banks do. Putting a lock-out for failures on the second pass boosts security.

Summary: slow down the attacker by using time-consuming hash functions. Also, block on too many accesses to your login, and add a second password tier.


4



For OAuth 2 passwords:

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

2