Пользователь
–0,2
рейтинг
17 ноября 2012 в 15:36

Разработка → Безопасное хранение паролей для сайтов на PHP tutorial recovery mode

По сей день очень многие хранят пароли в базе просто захешировав их с помощью md5 или в лучшем случае SHA-1, что едва ли обеспечивает сколь либо заметную безопасность. У устаревшего и уже признанного ненадёжным md5 существует немало коллизий, а также к нему рассчитаны довольно большие и общедоступные радужные таблицы. Ситуация с SHA-1 лишь немногим лучше.

Использование соли и более стойких алгоритмов хеширования значительно увеличивают надёжность, однако рассчёт даже длинных хешей — не очень сложная задача для современных систем, особенно с тех пор как для рассчётов стали привлекать GPU. Поэтому для увеличения надёжности шифрования паролей следует использовать такой алгоритм, который будет работать настолько медленно, что рассчёт радужных таблиц и атаки методом перебора станут абсолютно неэффективными. Это делает небезызвестная функция bcrypt. Для необратимого шифрования она использует алгоритм blowfish с множеством итераций, и работает очень медленно. Путём изменения количества итераций можно регулировать время, затрачиваемое на шифрования пароля, и найти такую «золотую середину», когда шифрование одного пароля будет достаточно быстрым, а подбор его станет бессмысленным.

В PHP есть встроенная функция crypt(), являющаяся реализацией bcrypt. С версиях PHP 5.3 и выше функция crypt() имеет встроенную реализацию алгоритмов шифрования и больше не зависит от поддержки этих алгоритмов операционной системой, чем и следует воспользоваться. Эта функция поддерживает различные алгоритмы, от DES до SHA-512, и выбирает алгоритм в зависимости от того, какая соль задаётся в качестве её параметра.

Ниже я хочу предложить простейший, состоящий из одной строчки генератор соли. Будучи скормленной функции crypt() данная соль заставит её использовать blowfish с 10 в квадрате итерациями. Это задаётся идентификатором $2a$ (использовать bcrypt), и идущим затем количеством итераций, которые задаются как логарифм по основанию 2 (в данном случае это 210, то есть 1024 итераций). Увеличение параметра на единицу удваивает количество итераций, и следовательно время рассчёта функции.

// Генерируем соль
$salt = '$2a$10$'.substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(),mt_rand()))), 0, 22) . '$';
// Шифруем пароль с применением данной соли
$hashed_password = crypt($password, $salt);


Соль будет длиной 30 байт, из которых 22 полезных и 8 служебных. На моём сервере (Xeon E5520, 2.24 ГГц) рассчёт одного пароля таким способом занимает 0.12 секунд — что недолго если мы проверяем правильность пароля, и очень много если мы массово подбираем пароли. На выходе будет получена строка длиной 60 байт.

Генератор позаимствован из комментариев на странице описания функции на php.net.
@varnav
карма
8,0
рейтинг –0,2
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

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

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

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

  • 0
    рассчёт одного пароля таким способом занимает 0.12 секунд — что очень много.


    Это же наоборот хорошо ;)
    • +3
      Да, это хорошо, об этом и речь. :)
      Для однократной проверки при логине 0.12 секунд — пшик, ерунда.
      Для перебора — убийственно долго.
      • +1
        Прошу прощения, подумал что Вы считаете это плохим :)
      • +4
        А не кажется ли вам, что и для DoS подобная штука удобна?
        Без captcha такие пароли лучше не запрашивать.
        • 0
          DoS на форму логина и в других случаях весьма удобен. Защиты от него стандартные. Капчу можно запрашивать после например 3-х неудачных попыток залогиниться.
        • +1
          На мой взгляд, лучше не (только) captcha, но и счетчик неудачных попыток аутентификации с данного IP. Если, к примеру, 5 неудачных попыток за последние сутки, тогда включать captcha для этого IP.
      • 0
        Пшик для не большого сайта с небольшой посещалкой. А если юзеров много?
        • +1
          Тогда вместо $2a$10$ пишете $2a$8$ и будет быстрее в 4 раза.
  • +2
    А с какой целью в заголовке указана MySQL?
    • 0
      Да, это лишнее. Исправлю.
  • –1
    В своё время нашел хороший класс на PHP как раз с bcrypt. Используется очень удобно:
    $bc = new Bcrypt(15); // Параметр - количество раундов хеширования. По умолчанию 12.
    //Хеширование
    $hash = $bc->hash($password);
    //Проверка
    $bc->verify($password, $hash)
    

    Соль генерирует сама, достаточно качественную, случайную.
    Ссылку уже не найду, если надо, могу прислать.
  • +4
    Вся статья это по сути описание функции crypt(). Причем менее полное, чем в руководстве пользователя.
    • +3
      Вся статья — это призыв использовать эту функцию. Потому что и сейчас её используют едва ли в половине случаев.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Слишком много кликов
    • +1
      Или OpenID
  • –2
    Почему просто не увеличивать время расчетов функциями типа sleep()/usleep()?
    • +3
      Потому что если сопрут базу, то это не поможет. Вы же не думаете, что пароли будут брутфорсить через веб?
      • –1
        А как они алгоритм/соль узнают, если сопрут только базу? )) Или мы уж совсем дикий сценарий рассматриваем?
        • +2
          Вы не параноик.
        • +6
          Априори в ИБ и криптографии считается, что алгоритм злоумышленнику известен.
        • +5
          Алгоритм легко понять если это хэширование.

          А вы соль где храните? Разве не в базе? Если не в базе — то одну на всех чтоли?
  • 0
    Друзья, я не совсем пойму, а после того как пароль захеширован, и сохранен в базе, рядом с ним нужно и соль хранить? Я так понимаю она случайно генерируется и для каждого пароля будет разная?
    • +1
      Вообще, вы это выбираете в реализации, но да, использование (и хранение) индивидуальной соли считается более безопасным, чем использование одной для всех. Большое значение это имеет при массовых атаках, чтобы максимально затруднить злоумышленникам возможность узнать пароли пользователей.
    • +2
      Да. Смысл индивидуальной соли в том чтобы не дать возможность сгенерировать одну радужную таблицу на все пароли. Поэтому скрывать её нет смысла да и негде.
  • +2
    Уважаемые хабровчане! Это моя первая статья на хабре. Как я вижу она ушла в минус. Так что первый блин — комом. Объясните пожалуйста новичку чем недовольны читатели.
    • +1
      У вас мысль на одно предложение и 1 строчку кода вылилось в целую статью (Мое ИМХО)
      • 0
        То есть стоило просто сообщить метод генерирования хэшей под bcrypt, потому что с bcrypt все знакомы?
        Но если это так — почему так часто в сети возникают сообщения о том что украдена база паролей и легко расшифрована?
        • 0
          По теме, я точно не могу сказать — я не совсем ЦА данной статьи и потому не сужу ее, а лишь предположил.
          Но если это так — почему так часто в сети возникают сообщения о том что украдена база паролей и легко расшифрована?

          Потому же, почему и люди перебегают дорогу в неположенном месте, поворачивают через две сплошных, нарушают скоростной режим, не используют зубную нить, не ходят к стоматологу раз в пол года. Потому что «и так сойдет»
          • 0
            Возможно что если бы это была статья о том что в автомобиле надо пристёгиваться и как это правильно делать — её бы заминусовали те кто не пристёгивается, а таких очень много.
  • 0
    По теме: Насколько есть смысл генерировать соль на основе чего-либо. Например, «солить» логином пользователя или функцией от него. Есть ли смысл действительно генерировать и хранить соль? Мне кажется, для защиты от радужных таблиц, достаточно просто посолить пароль логином\useriD\email-ом\Тем-что-не-предусмотрено-менять, нет?
    • 0
      Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 digits from the alphabet "./0-9A-Za-z".
      • 0
        ну, я имел ввиду есть ли смысл генерации отдельной соли и хранения в отдельном поле, или вполне можно использовать любой другой параметр, например (не линчуйте)
        substr(str_repeat(base32($username),$n),0,22)
        • 0
          Думаю фактически можно и так. Просто некоторое кол-во произвольности внутри готового шифрованного пароля теоретически увеличивает его безопасность.
          • 0
            >произвольности внутри
            Так соль же рядом лежит. так что она не произвольность, А конкретная строка, и не все ли равно из чего она берется — если выход приблизительно один?
  • –1
    Похоже моя первая статья оказалась неудачной, я её перемещу в «черновики» если к понедельнику не будет в плюсе. Пусть остаются только хорошие статьи.
    • +1
      Не подводите тех 90 что добавили в избранное :)
      • 0
        А почему добавляют статью в избранное если она по мнению участников — неудачная?
        • 0
          Потому что очень много людей не могут оценить неудачность статьи ввиду отсутствия необходимой квалификации.

          Даже если бы вы написали статью «меняйте двойные кавычки на одинарные и у вас код станет быстрее в 2 раза» — то и её бы добавляли в избранное.
          • +1
            А, ну тогда ничего плохого не будет если я её удалю.

            Но надо сказать что я в очень многих примерах в сети и в очень многих программах встречаю двойные кавычки там, где они не нужны. Может быть стоило бы написать статью и про это. Но я не буду.
            • 0
              Ну как угодно. А двойные кавычки вставляем я, zerkms и контрольный пакет PHPшников. Потому что это непринципиально — наносекунды не делают погоды ;)
              • +1
                А вот небезопасно хранимые пароли погоду делают, но их хранят в простых хэшах даже программисты крупнейших сайтов — что регулярно подтверждается громкими утечками с расшифровкой.
  • +2
    Есть мнение, что bcrypt использовать опасно, т.к. ее никто особо не подвергал криптоанализу. Да, она достаточно надежная, и если вы ее уже используете, то в ближайшие пару лет вам беспокоиться не о чем. Но если вы начинаете новый проект, то вам, возможно, стоит обратить внимание на стандартизированную хорошо исследованную функцию PBKDF2.

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