PHP
22 June 2013

PHP 5.5 «API хэширования паролей»

From Sandbox
Вот и вышел финальный релиз PHP 5.5.0. Кратко о новых возможностях можно прочитать в посте на официальном сайте или «Переведенное на русский».

На хабре уже были статьи о некоторых новых возможностях PHP 5.5.0, такие как «Coroutines в PHP и работа с неблокирующими функциями» и «В PHP 5.5 возможно появится Finally»
В данной статье будет затронута одна из новых возможностей PHP 5.5.0 "API хэширования паролей".Предоставляющий застрахованные от ошибок разработчиков и более простые в использовании высокоуровневые функции для генерации и проверки валидности паролей по хэшам. Основное отличие нового API в том, что он берёт на себя генерацию надёжных хэшей, скрывая от разработчика операции ручного указания salt-а и выбора алгоритма хэширования (по умолчанию используется Bcrypt). Создание хэша сведено к выполнению "$hash = password_hash($password, PASSWORD_DEFAULT);", а проверка к вызову «password_verify($password, $hash)». В качестве причины внедрения нового API послужило безалаберное отношение многих разработчиков к генерации salt-ов и повсеместный выбор нестойких к перебору алгоритмов хэширования.

Будут рассмотрены константы, функции и код, использующий их.

Предопределенные константы
Перечисленные ниже константы всегда доступны как часть ядра PHP.

PASSWORD_BCRYPT (integer) = 1
PASSWORD_BCRYPT используется для создания нового хэш пароля с использованием алгоритма CRYPT_BLOWFISH.

PASSWORD_DEFAULT (integer) = PASSWORD_BCRYPT
Используется алгоритм хэширования по умолчанию, если алгоритм не задан. Он может измениться в новых версиях PHP, когда будут поддерживаться новые, более эффективные (например, Scrypt) алгоритмы хэширования.

Функции хэширования паролей




array password_get_info ( string $hash ) — Возвращает информацию о данном хэше.

Возвращает ассоциативный массив с тремя элементами(ключами):
— алгоритм(algo), который будет соответствовать константе алгоритма пароля;
— название алгоритма(algoName), которое имеет человечески читаемое название алгоритма;
— ассоциативный массив с опциями (options), который включает в себя возможности, предоставляемые при вызове password_hash ().




string password_hash ( string $password, integer $algo [, array $options ] ) — создает новый хэш пароля.
  • password-пользовательский пароль.
  • algo — константа, обозначающая используемый алгоритм хэширования пароля. Вы можете использовать алгоритм по умолчанию постоянно, если вы хотите, чтобы алгоритм автоматически обновлялся в более эффективный, доступный в новых версиях PHP.
  • options — ассоциативный массив с опциями. В данный момент поддерживаются только 2 опции: salt — соль, используемая при хэшировании пароля, и cost, обозначающая алгоритмическую стоимость вычисления пароля. Примеры данных значений можно найти на странице документации функции crypt(). Если он опущен, будет создана случайная соль и будет использоваться алгоритмическая стоимость вычисления по умолчанию.

Возвращает зашифрованный пароль или FALSE в случае возникновения ошибки.




boolean password_needs_rehash ( string $hash, string $algo [, string $options ] ) — проверяет, соответствует ли предоставленный хэш заданному алгоритму и опциям. Если нет, то считается, что хэш должен быть изменён.
  • hash — хэш, созданный функцией password_hash().
  • algo — константа, обозначающая используемый алгоритм хэширования пароля.
  • options — ассоциативный массив с опциями. В данный момент поддерживаются только 2 опции: salt — соль, используемая при хэшировании пароля, и cost, обозначающая алгоритмическую стоимость вычисления пароля. Примеры данных значений можно найти на странице документации функции crypt(). Если он опущен, будет использоваться алгоритмическая стоимость вычисления по умолчанию.

Возвращает TRUE, если хэш должн быть изменён, чтобы соответствовать данному алгоритму и опциям, или в противном случае FALSE.




boolean password_verify ( string $password, string $hash ) — Проверяет, соответствие пароля с заданным хэшем. Этот хэш может быть создан с помощью password_hash () или обычный crypt() хэш.
  • password-пользовательский пароль.
  • hash — хэш, созданныйфункцией password_hash().

Возвращает TRUE, если пароль и хэш соответствуют или в противном случае FALSE.




Код и вывод результата

<?php

$options = [
     'cost' => 7,
     'salt' => 'BCryptRequires22Chrcts',
];

$hash['hash'][] = password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options);
//"$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq"
$hash['hash'][] = password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
//"$2y$10$hHi0De9WN.HL6.Fz1ElvbOMIU5NA0tetwdJzNziKJvHFXFqOxsybi"

$hash['info'][] = password_get_info($hash['hash'][0]);
//array("algo" => 1 , "algoName" => "bcrypt" , "options" => array("cost" => 7 ))
$hash['info'][] = password_get_info($hash['hash'][1]);
//array("algo" => 1 , "algoName" => "bcrypt" , "options" => array("cost" => 10 ))

$hash['rehash'][] = password_needs_rehash($hash['hash'][0],PASSWORD_BCRYPT,$options);  //false
$hash['rehash'][] = password_needs_rehash($hash['hash'][0],PASSWORD_DEFAULT);  //true
$hash['rehash'][] = password_needs_rehash($hash['hash'][1],PASSWORD_DEFAULT);  //false

$hash['pas_verify'][] = password_verify('rasmuslerdorf', $hash['hash'][0]); //true
$hash['pas_verify'][] = password_verify('rasmuslerdorf', $hash['hash'][1]);  //true
$hash['pas_verify'][] = password_verify('rasmuslerdorff', $hash['hash'][0]);  //false
$hash['pas_verify'][] = password_verify('rasmuslerdorff', $hash['hash'][1]); //false

var_dump($hash);

Вывод результата
array(4) {
  ["hash"]=>
  array(2) {
    [0] => string(60) "$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq"
    [1] => string(60) "$2y$10$CYb5tz9f5IVAgqX7SkIv9ufbi6yYlMQgAHcV4ixXjYSHJZl9KwLrK"
  }
  ["info"]=>
  array(2) {
    [0]=>
    array(3) {
      ["algo"] => int(1)
      ["algoName" ]=> string(6) "bcrypt"
      ["options"] => array(1) {
                          ["cost"] => int(7)
                     }
    }
    [1]=>
    array(3) {
      ["algo"] => int(1)
      ["algoName"] => string(6) "bcrypt"
      ["options"] => array(1) {
                       ["cost"] =>  int(10)
                    }
    }
  }
  ["rehash"]=>
  array(3) {
    [0] => bool(false)
    [1] => bool(true)
    [2] => bool(false)
  }
  ["pas_verify"]=>
  array(4) {
    [0] => bool(true)
    [1] => bool(true)
    [2] => bool(false)
    [3] => bool(false)
  }
}


Также на сайте 3v4l.org можно посмотреть VLD опкоды и сравнителную производительность на различных версиях PHP. Вот например производительность кода с статьи.
Version System time User time Max. memory usage
5.5.0alpha1 0,018 s 0,267 s 12,152 MiB
5.5.0alpha2 0,018 s 0,267 s 12,148 MiB
5.5.0alpha3 0,015 s 0,271 s 12,148 MiB
5.5.0alpha4 0,019 s 0,268 s 12,164 MiB
5.5.0alpha5 0,014 s 0,270 s 12,195 MiB
5.5.0alpha6 0,016 s 0,304 s 12,219 MiB
5.5.0beta1 0,017 s 0,270 s 12,270 MiB
5.5.0beta2 0,027 s 0,294 s 12,270 MiB
5.5.0beta3 0,022 s 0,265 s 12,656 MiB
5.5.0beta4 0,016 s 0,299 s 12,656 MiB

К сожалению финального 5.5.0 релиза пока ещё в списке нет.

Возможные будущие обновления
На данный момент алгоритм шифрования по умолчанию (пока что поддерживается только один) определяет предустановленная константа PASSWORD_DEFAULT. В новых версиях языка алгоритм шифрования по умолчанию можно будет задать в настройках.

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

Будущие Опасения
С природой криптографии, будущая совместимость является предметом серьезного беспокойства. Для того, чтобы быть в безопасности, эти функции должны будут адаптироваться к меняющимся требованиям в будущем.

Ссылки:
Документация по API хэширования паролей
Request for Comments: Adding simple password hashing API — Anthony Ferrara
Бета версия нового сайта PHP.net

P.S от EugeneOZ:
Эти функции можно использовать уже сейчас и без изменения кода перейти на нативные.
Всего лишь нужно использовать этот файл: github.com/ircmaxell/password_compat/blob/master/lib/password.php (Необходим PHP> = 5.3.7 )
Который написан тем же человеком, который написал функции в коде PHP — Anthony Ferrara

P.S орфографические ошибки и ошибки перевода пишите в лс

Благодарности:
Спасибо
nixmale, newdya, how за указанные ошибки.
EugeneOZ за дополнение.
@yourway за изменение «Возможные будущие обновления» в более понятный вариант.

+38
13.4k 142
Comments 28