0,0
рейтинг
22 июня 2013 в 01:15

Разработка → PHP 5.5 «API хэширования паролей» из песочницы

PHP*
Вот и вышел финальный релиз 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 за изменение «Возможные будущие обновления» в более понятный вариант.
Дмитрий Королёв @Chameleoh
карма
18,0
рейтинг 0,0
Программист
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (28)

  • 0
    Интересно, чем существующая crypt их не устроила? и зачем был нужен еще один велосипед?
    • +1
      Я так понял, что криворукостью и безалаберностью многих web-разработчиков, которые или не занимались безопасностью хранения паролей вообще (например, хранили в открытом или почти открытом виде), или реализовывали «на отвяжись» (просто md5 без соли, например).
      А тут название функции как бы говорит разработчику: для паролей используй меня, девелопернейм, и будет всё труЪ.
      • 0
        Совершенно верно, причем такая безалаберность была даже на крупных, известных проектах.
        Например:
        LinkedIn.com — хранили пароли в SHA-1 и возможно даже без соли вот и похудели на 6,5 миллионов аккаунтов.
        Last.fm — 40 млн, hotmail.com, msn.com, live.com и др.

        И всё из-за того же.
      • 0
        криворукостью и безалаберностью многих web-разработчиков, которые или не занимались безопасностью

        Интересно, как изобретение ещё одного велика, позволит выпрямить руки, сделать разрабов залаберными и заставит их заняться безопасностью?

        О шифровании паролей односторонним алгоритмом написано сейчас везде во всех статьях, книгах и документациях, однако, ленивые разрабы остаются ленивыми.
        • 0
          Да вот тоже думаю, что никак. Есть ощущение, что про этот функционал многие могут даже и не узнать. Как-то не принято что ли у людей изучать и использовать новый функционал. Но время покажет, конечно.
  • –3
    Насколько безопасно это решение? Хранить соль хеширования совместно с хешем?
    • 0
      имхо полностью. соль нужна только для того, чтобы по предварительно сгенерированной таблице хэшей пароль не подобрали.
      • 0
        Вот из википедии:

        Где хранить соль? Не опасно ли хранить ее в открытом виде? Можно ли поместить соль в код и ее использовать для всех паролей?

        Все, что может быть украдено, будет украдено. Если вы уверены в защищенности кода, то свой алгоритм хеширования поможет лучше соли. Помните — соль не защищает один конкретный хеш от перебора, поэтому нет цели прятать соль — она хранится в открытом виде рядом с хешем. Задача соли — спасти набор украденных хешей, «удлинняя» пароль, а сделать она это может только в том случае, если у каждого хеша будет своя соль. Поэтому мы храним соль рядом с хешем и для каждого хеша генерируем свою уникальную последовательность символов соли.
        • –2
          Да, в википедии такой бред не редко попадается.
          Конечно, соль лучше прятать отдельно. Зная соль, можно быстро построить радужные таблицы и пройтись по словарю.

          • +1
            Тогда вопрос номер раз: где именно её прятать?
            Вопрос номер два: в каких продуктах такое есть?

            Всякие друпалы не берём. В OTRS, по-моему, соль рядом с хэшем лежит. В Linux («теневые пароли») пароль солят, по-моему, то ли uid/guid'ом, то ли логином. И т.д.
            Да и при условии, что для каждого пароля собственная соль (а так и должно быть) таблицу придётся строить для каждого пароля (о чём, кстати, во всеми нелюбимой википедии написано). В том и смысл соли: не дать гарантию, что пароль невозможно подобрать, а максимально усложнить подбор.
            • 0
              у меня постоянно происходит конфликт мыслей. и паранойя не уменьшается даже посолив пароли. и не покидают голову следующие мысли:

              вся суть защиты паролей солями и хешами — это усложнить взлом паролей в уже украденной базе.
              но вот, тут и проблема. базу кто ворует? очевидно те у кого есть доступ к ней. Но в подавляющем большинстве, те кто имеют доступ к базе — имеют его и к исходникам. Так, как же сработает эта защита, если украли все, исходники и базу?

              Или все таки есть способ защитить хеши так что при потери исходников и базы — взлом был «невозможен»?
              • 0
                ИМХО потеря исходников не грозит ничем (при условии, что прочитав исходники нельзя найти дыры в системе).
                Хэши паролей можно потерять по-разному: может сисадмин (или какой-то другой сотрудник) «подсмотреть», могут бэкап сдёрнуть, могут доступ на сервер получить (причём при правильно организации прав доступа сам факт доступа к серверу ещё не означает фатальную угрозу). В крайнем случае, если даже получен root-доступ, вы можете быстро реальную систему перекинуть на другой сервер при этом пользователи не пострадают (но, конечно, лучше попросить сменить пароли к системе).

                Конечно, если вы не наблюдаете за системой и она долгое время живёт «автономно», то ничего хорошего в доступе неавторизованного лица нет — но это уже не проблема хранения паролей / соли.
                • 0
                  потеря исходников не грозит ничем

                  дргими словами вскрытие алгоритма самого соления ничем не грозит? можно подробнее этот момент.
                  • 0
                    Ничем… Или должен быть очень хитрый алгоритм (который фактом своего существования как-то защищает открытый пароль).
                    Во-первых, есть куча opensource проектов, где алгоритм соления известен всем.
                    Во-вторых, имея регистрацию в системе, жэш и соль пароля, я с определённой вероятностью (для большинства проектов) могу раскрыть алгоритм соления.
                    И самое главное: соление не гарантирует взломоустойчивость, оно лишь усложняет (замедляет) взлом.
                    По сравнению со скоростью перебора солёных паролей, скорость изучения алгоритма соления ничтожно мала.
                    • 0
                      А как его изучить то? Если соль спрятана в исходниках скрипта, и нам известен только хеш. Даже если взять соль состоящую из даты регистрации йдишника и секретной фразы, из хеша алгоритм соления не получится получить.
                      • 0
                        Алгоритмы я-ля md5($salt. md5($password)) при наличии хэша, соли и самого пароля подбираются на ура (а таких алгоритмов большинство).

                        Другой вопрос в том, что значит «соль спрятана в исходниках скрипта»? Одна соль на все пароли? Такого не должно быть.
                        • 0
                          Вот о том и речь. Что нужно знать соль чтобы раскрыть алгоритм каким образом генерируется хэш. Если соль является частью хэша, то искать её даже не нужно, берем соль генерируем таблицу хэшей и пошли перебирать.

                          Просто когда хэш и соль хранится в разных местах, сложнее добыть и то и другое. Стянул базу не добрался до кода на php. А так получается утянул базу у тебя есть и соль и хэш.
                          • 0
                            ИМХО если факт публикации алгоритма шифрования / хэширования или чего-либо другого относящегося к криптографии способен нанести ущерб безопасности, то это плохой алгоритм (но при этом допускаю, что в определённых ситуациях закрытый алгоритм поможет избежать дополнительных проблем). С моей точки зрения, взломщику больше усложнит жизнь, например, скорость генерации хэшей (при применении соответствующих алгоритмов).
                        • 0
                          Я, например, солю всегда каким-то статическим параметром из базы. Например, так: hash(«sha256», $user->id.$user->register_time.$user->password); Если исходники не утянут, то секьюрно вроде бы. Если утянут, правда, то ничего не спасёт.
                  • 0
                    Считается, что алгоритм шифрования заранее известен злоумышленнику. Соль можно считать частью алгоритма шифрования.

                    И ещё, по идее соль должна быть случайной.
            • 0
              Естественно, лучше айца хранить по отдельным корзинам, например, в серверном коде соль, в базе лишь хеш. Есть системы, которые требуют чтобы эти данные хранились лишь в оперативной памяти, тогда даже кража сервака не так страшна. В том же линукс прямой доступ к shadow имеет телько root, остальные делают запросы через сервис.

              В вэбе большая часть пользователей используют в качестве пароля обычные слова-дату, поэтому зная соль по хешу можно очень быстро найти пароли по словарю.

              • 0
                > Естественно, лучше айца хранить по отдельным корзинам
                Это бесспорно :)

                > например, в серверном коде соль
                Одна соль для ВСЕХ пользователей? Она тогда резко теряет смысл, разве нет?
                Или вы про то, что её как-то динамически генерировать для каждого пользователя?

                > В том же линукс прямой доступ к shadow имеет телько root, остальные делают запросы через сервис.
                Ну так и в вебе, вроде, хэши не публичная инфа. Речь идет про компрометацию системы или хэшей.
          • 0
            «Быстро построить» это насколько быстро? Даже если соль известна, но индивидуальна для каждого пароля, таблицы придётся строить для каждого пароля отдельно.
  • –1
    > P.S после 15 сек как добавил статью карма -1,0 и рейтинг –0,5, спасибо за «хорошие» впечатления о данном ресурсе на 1ом же посте.

    Chameleoh, не парься, хабр не для адекватных людей… статьи лучше пиши в своем блоге, а тут читай желтуху и иногда что нибуть интересное…
  • 0
    Интересно, что им мешало сделать PBKDF2?
  • 0
    (facepalm) password_needs_rehash
    • 0
      нормальная функция, позволит проекту плавно мигрировать с одного алгоритма хеширования на другой по мере обновления функциональности PHP автоматически

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.