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

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

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

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

9 ответов

Решение

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)

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

Использование PHP <5.3.7 (УСТАРЕЛО)

Ты можешь использовать 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.

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

Причина проста: так легко облажать 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
    }
}

Ресурсы

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

О компании BCrypt

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

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

Есть много разных вариантов. Что вы выбираете, зависит от вас. Тем не менее, я НАСТОЯТЕЛЬНО рекомендую вам использовать одну из перечисленных выше библиотек для решения этой проблемы.

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

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

Вы получите достаточно информации в "Радужных таблицах": что нужно знать о безопасных схемах паролей или портативной среде хеширования паролей PHP.

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

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

PHP - функция Crypt


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

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

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

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

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

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

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

СИНТАКСИС

string password_hash($password , $algo , $options)

В настоящее время функцией password_hash() поддерживаются следующие алгоритмы:

PASSWORD_DEFAULTPASSWORD_BCRYPTPASSWORD_ARGON2IPASSWORD_ARGON2ID

Параметры: эта функция принимает три параметра, как указано выше и описано ниже:

пароль: хранит пароль пользователя.algo: это константа алгоритма пароля, которая используется постоянно при обозначении алгоритма, который должен использоваться при хешировании пароля.options: это ассоциативный массив, который содержит параметры. Если он удален и не включен, будет использоваться случайная соль, и произойдет использование стоимости по умолчанию.Возвращаемое значение: возвращает хешированный пароль в случае успеха или False в случае неудачи.

Пример:

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Ниже приведены программы, иллюстрирующие функцию password_hash() в PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

ВЫХОД

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

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

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

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

функция принимает 2 стандартных параметра ($ пароль и $ алгоритм) и 1 необязательный параметр ($options).

$strongPassword = password_hash( $password, $algorithm, $options )


Прямо сейчас для password_hash() разрешены следующие алгоритмы строгого текста:

  • PASSWORD_DEFAULT

  • PASSWORD_BCRYPT

  • ASSWORD_ARGON2I

  • PASSWORD_ARGON2ID


пример: echo password_hash("abcDEF", PASSWORD_DEFAULT);

ответ: $2y$10$KwKceUaG84WInAif5ehdZOkE4kHPWTLp0ZK5a5OU2EbtdwQ9YIcGy


пример: ʻecho password_hash("abcDEF", PASSWORD_BCRYPT);`

ответ:$2y$10$SNly5bFzB/R6OVbBMq1bj.yiOZdsk6Mwgqi4BLR2sqdCvMyv/AyL2


чтобы использовать BCRYPT в качестве пароля, используйте опцию cost =12 в массиве, а также измените 1-й параметр $ password на какой-нибудь надежный пароль, например "wgt167yuWBGY@#1987__"

Пример: echo password_hash("wgt167yuWBGY@#1987__", PASSWORD_BCRYPT ,['cost' => 12]);

Ответ: $2y$12$TjSggXiFSidD63E.QP8PJOds2texJfsk/82VaNU8XRZ/niZhzkJ6S

Другие вопросы по тегам