Как испортить безопасность паролей, следуя советам с Хабра

    В недавней статье «Как надо хешировать пароли и как не надо» было озвучено предложение использовать некий локальный параметр (его еще называют перцем), как еще один рубеж защиты паролей. Несмотря на то, что это решение создает больше проблем, чем решает, большинство комментаторов поддержали эту идею, а несогласных, как водится, заминусовали. Мир в моей голове рухнул и я решил написать эту статью.

    Суть


    Перец — такая глобальная случайная строка, которая дописывается ко всем паролям (помимо соли). Она секретна (в отличии от соли). Таким образом, получив базу, узнать пароли становится невозможно. И все, казалось бы, хорошо…

    Проблема инвалидации


    Перец глобален, по своей сути. Если Вы захотите его сменить, например, в случае утечки, то Вам придется сменить все пароли.

    Проблема хранения


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

    Проблема «иллюзии защищенности»


    Перец, как бы «усиливает» слабый пароль, но только в недрах системы. Через форму логина пароль «asdf» подбирается все так же легко. Слабый пароль — это слабый пароль, как бы Вы себя не обманывали.

    Проблема реализации


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

    Demo time


    Опытный разработчик знает, что хеш должен быть медленным и соленым, поэтому он использует bcrypt.
    Прочитав статью на Хабре он решает усилить это все перцем.
    И к нему на сайт приходит пользователь, причем продвинутый, знающий, что пароли должны быть сложными:

    <?php
    
    // Наш перец. Невероятно непредсказуем. Хакеры в панике.
    $pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>';
    
    // Наш пользователь - молодец. Пароль чертовски хорош.
    $password = 'E&z89Usr?R7VF.^';
    
    // Хешируем!
    $hash = password_hash($pepper . $password, PASSWORD_BCRYPT);
    var_dump($hash);
    //string(60) "$2y$10$0V95jRy9I.P3t7YRiMHT3O7JEveN1Gya/LbvNJ.H6K1mVPxPFRsUm"
    // У Вас будет другой хеш, т.к. соль генерируется автоматически
    

    А теперь, логин (следите за руками!):

    <?php
    
    // Наш глобальный перец
    $pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>';
    
    // Хеш нашего пользователя (подставьте свое значение)
    $hash = '$2y$10$0V95jRy9I.P3t7YRiMHT3O7JEveN1Gya/LbvNJ.H6K1mVPxPFRsUm';
    
    // Нет! Это же неправильный пароль! Ты же не сможешь войти!
    $password = 'habrahabr';
    
    // Проверяем соответствие хеша и пароля
    echo password_verify($pepper . $password, $hash) ? 'Login OK' : 'Wrong password';
    // Login OK
    // WTF???
    

    Таким вот нехитрым способом мы помножили всю нашу защиту на ноль.

    Обновление поста


    Ругаете PHP?
    Попробуйте это:

    #!/usr/bin/env python
    
    import bcrypt
    
    pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>'
    password = 'E&z89Usr?R7VF.^'
    hashed = bcrypt.hashpw(pepper + password, bcrypt.gensalt())
    print(hashed)
    
    password = 'habrahabr'
    if bcrypt.hashpw(pepper + password, hashed) == hashed:
        print('Login OK')
    else:
        print('Wrong password')
    

    Вам очевидно, что нужно дописывать в конец?
    С чего вы взяли, что это будет работать? Есть RFC где такое написано?
    Куда бы вы не дописывали перец — это велосипед.

    То, что я описал в статье — боян и давно известно?
    А что же вы об этом молчали? Как-то я ни разу не встретил комментария на Хабре, в котором бы вы предупреждали о возможных проблемах.
    Ах, ну да, это же очевидно.
    Share post

    Similar posts

    Comments 135

      +28
      «есть миллион способов наделать ошибок с криптографией» (src)
        –10
        Про миллион способов — это однозначно да!

        А вот по поводу статьи… не пойму всеобщей истерии вообще.
        Мир в моей голове рухнул и я решил написать эту статью.

        К сведенью ярых плюсующих — этому баяну уже года полтора как, и на эти грабли уже полмира понаступало…
          +4
          И ведь продолжают наступать!
          –3
          Все же довольно очевидно, надо всегда ставить пароль пользователя в начале, потом соль и потом уже перец
          Всегда делал это по-наитию ;)
          –22
          пять, давай зачётку.
            +11
            почему так происходит?
              +35
              Caution

              Using the PASSWORD_BCRYPT for the algo parameter, will result in the password parameter being truncated to a maximum length of 72 characters. This is only a concern if are using the same salt to hash strings with this algorithm that are over 72 bytes in length, as this will result in those hashes being identical.

              password-hash

              Т.е. в данном случае при использовании Bcrypt максимальная длина пароля — 72 байта. Все что больше — обрезается.
              В приведенном коде получается что роль пароля играет (т.е. хэшируется) только перец.
              Собственно я никогда не задумывался, что можно солить или перчить именно в таком порядке — я предпочитаю сначало пароль — а потом уже все остальное.
              +55
              Помножил защиту на ноль разработчик password_hash/password_verify игнорируя то, что переданный ему параметр длиннее 72 байтов и не выкидывая exception (что является самым логичным, т.к. разработчик явно ошибся и ничего не работает) молча обрубает. Разработчик тоже хорош, т.к. невнимательно читает документацию к используемым функциям, но он по определению менее квалифицирован, чем разработчик библиотеки и его вина тут меньшая.

              А локальная соль («перец» в вашей терминологии) это правильный способ, т.к. спасает от важного класса атак — компрометации БД без компрометации кода приложения. Это действительно частая атака, т.к. для дампа таблицы юзеров порой достаточно одного sql injection-а. А вот код приложения сдампить при sql-injection-е может и не получиться, особенно если он read-only.

              Вообще хорошим способом подобной защиты может являться некое устройство, которому на вход подаётся строка, а на выходе она же, но захешированная с помощью некоего недоступного ключевого параметра. Тогда и код скомпрометированный не откроет пароли и устройство не «скачаешь». Этим устройством может быть простой маленький компьютер с простеньким веб-сервисом, например.
                +4
                > Разработчик тоже хорош, т.к. невнимательно читает документацию к используемым функциям
                Конечно хорош, это же опытный разработчик. Ну, по заверению автора :)

                > он по определению менее квалифицирован
                Каким определением пользуетесь?

                Имхо, здесь разработчик-пользователь password_hash — ССЗБ.
                  +4
                  > Каким определением пользуетесь?

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

                  > Имхо, здесь разработчик-пользователь password_hash — ССЗБ.

                  Я так понимаю, это стандартная библиотечная функция PHP. Если это не так и имеется в виду, что программист сам сделал функцию с таким ограничением и сам про это забыл, тогда да, ССЗБ.
                  +2
                  Помножил защиту на ноль разработчик password_hash/password_verify игнорируя то, что переданный ему параметр длиннее 72 байтов и не выкидывая exception

                  У авторов PHP есть давняя традиция делать странно… Хотя у меня возникали надежды, что они взялись за ум после выпиливания «дружественных новичку» «фич».
                    +6
                    Там апдейт в статье.
                    А теперь дайте характеристику авторам питона, а я пока схожу за попкорном.
                      +4
                      Ха-ха, так это ограничение bcrypt. Ради обучения масс можно было упомянуть об этом в статье. :)
                        0
                        Причём тут ограничение bcrypt если речь о том, что в библиотеках нет проверки входных данных и выкидывания исключения?
                    –3
                    Другой важный тип атак — радужные таблицы. Такую таблицу даже если у вас используется самая простая соль придётся генерировать заново.
                      +1
                      > А вот код приложения сдампить при sql-injection-е может и не получиться, особенно если он read-only.

                      Если код доступен только для чтения, то его нельзя прочитать, я правильно понял? :)

                      А ещё случайный человек в IRC рассказывал, что LOAD DATA INFILE помогал ему читать очень разные файлы.
                        0
                        Если код доступен только для чтения, то его нельзя прочитать, я правильно понял? :)


                        Я имел в виду read only injection. Например можете подставлять $table в «select * from $table». А update уже подставить не получается по каким-либо причинам (если получается подставить, то можно себя сделать админом и через админку попробовать поломать, которая обычно не так продумана, как общедоступный интерфейс, а может и не получится).

                        А ещё случайный человек в IRC рассказывал, что LOAD DATA INFILE помогал ему читать очень разные файлы.


                        Хитрые админы могут там всяких разных пользователей завести для разных задач, для БД одного, для приложения другого, и читать они ничего друг у друга не могут. Вроде стандартная практика. Или вообще разные серверы/контейнеры.
                      +32
                      Пересолил
                        +33
                        Переперчил же
                          +4
                          Наверняка влюбился
                          +17
                          По поводу примера:
                          1) длинна «перца» (и соли — если есть в реализации) подбирается под конкретный алгоритм;
                          2) RTFM; Копипастеры, блин… А потом говорят, что в пхп быдлокодят.
                          3) это даже не «ошибка», а вопрос конкретной реализации, конкретной функции, в конкретном языке;
                          3) правильно солить нужно уметь и без перца (тут известная поговорка про голову и шапку).
                          4) если вы например для хэша отведете в базе 10 байт, а алгоритм требует 32, и у вас оно обрежется, то это тоже ошибка хабра (потому-что вам об этом не сказали).

                          Продолжать?
                            +2
                            Что мешает добавлять перец после пароля, а не до?
                              +1
                              При такой реализации, все мешает.
                              Хотя бы тот факт, что если завтра ее поправят:
                              1) с обрезаным буфером, все пользователи тогда идут лесом;
                              2) даже без перца, ни один пользователь с паролем длиннее 72 байт не зайдет в систему
                                0
                                Причём тут это? Проверять длинну пароля можно ДО добавления перца. А перец добавлять уже после (а бикрипт сам урежет до 72 байтов лишнее) итого для слабых паролей добавляется какая никакая, но защита.
                                  –2
                                  Поясню кодом:

                                  <?php
                                  echo password_verify($password. $pepper, $hash) ? 'Login OK' : 'Wrong password';
                                  
                                    +5
                                    Завтра правят эту конкретную реализацию в пхп, которая СЕЙЧАС обрезает (и она больше не обрезает).
                                    Выход один резать самому, пока не поменяли все пароли…
                                    Кроме того в криптоанализе этот алгоритм теряет оценку, ибо резать соленый пароль нильзя:
                                    — вероятность обнаружения коллизии на буфере известной длинны выше на несколько порядков, чем если он нам неизвестен;
                                    — вероятность обнаружения способа упростить перебор либо использовать такую коллизию при переборе выше на порядок;
                                    — скорость перебора на тех же FPGA растет однозначно на несколько порядков (FPGA критичны при доступах к памяти и зная длинну последовательности, перестроив алгоритм мы ускоряем перебор неимоверно).
                                      0
                                      Завтра правят эту конкретную реализацию в пхп, которая СЕЙЧАС обрезает (и она больше не обрезает).

                                      Если её поменяют на одном сайте из десяти отвалится регистрация. Но это так, чем конкретно идея перца плоха, подбирать 72-х байтный пароль дело вообще не шуточное (и уж если на то пошло, то имея оборудование для быстрого взлома 72-х байтного пароля, то уже как-то вообще наплевать на длину этого самого пароля), особенно с учётом того, что даже подобрав нужный пароль, хакер всё равно не узнает перец и не сможет авторизоваться.
                                        +1
                                        Теоретически вы правы, практически это как раз то, что все по поводу и нет называют «security through obscurity». Потому что вы вводили перец чтобы усилить безопасность, а такой реализацией вы ее ослабили.
                                        При нормальной реализации если перец сольют, ничего не изменится, здесь же не так — т.к. мы знаем как минимум длинну пароля…
                                        Но это так, чем конкретно идея перца плоха
                                        Она не плоха, и я завсегда за. Но если с головой!
                                          0
                                          Потому что вы вводили перец чтобы усилить безопасность, а такой реализацией вы ее ослабили

                                          Чем я её ослабил? Тем что хакер угадает некую последовательность символов, которые при хэшировании идентичны хэшу пароля?
                                          Только вот авторизоваться он всё равно не сможет.
                                            0
                                            Вероятностно, вы ее ослабили, не добавлением «перца» — нет, а реализацией этого добавления. Причины ослабления описал выше.
                                            Я за «перец», я против необдуманного решения. Эту пхпшную функцию, в том виде как сейчас нельзя пользовать с «перцем», если длинна всей хэшируемой последовательности (вместе или нет) больше 72 байт.
                                              +1
                                              В случае же, если автообрезание до 72-х байт вырежут — не составит труда дописать это обрезание самостоятельно (конечно по-хорошему туда нельзя посылать строки больше 72-байт, однако мы не о хорошем примере сейчас, а конкретно о перце)
                                              Перец при добавлении в конец пароли никоим образом не может ослабить пароль.
                                                +1
                                                Я же вам написал, что теоретически вы правы… Теоретически оно даже не отличается от добиванием нулями. Вероятностно же (еще раз), конкретно эта реализация, особенно с bcrypt'ом слабее такой же точно без обрезки. Кстати, вы знаете почему в серьезных изданиях bcrypt подвергается критике? — изначально он имел ошибку в алгоритме и резал 55 байт. Теперь он имеет алгоритмическое ограничение ат дезайн в 72 байта.

                                                Байка, которую описал автор — тот еще баян, его вообще где только не обсуждали уже, начиная от stackexchange и заканчивая серьезными изданиями… (видел где-то на просторах просто отличное, но не найду чего-то).
                                                  0
                                                  Вы опять меня не слышите и повторяете про bcrypt, принцип перца не меняется в зависимости от алгоритма, нужно только учитывать максимальную длину строки.
                                                  Повторюсь ещё раз: перец не меняет алгоритм. Перец позволяет усложнить хакеру задачу, когда он добыл уже и соль и некую последовательность которая даёт тот же хэш, т.к. этой последовательности всё равно будет добавлен перец, то хакер всё равно не сможет авторизоваться.
                                                    +1
                                                    Да нет, это вы не слышите — я вам говорю же что не против «перца». Я против этой конкретной и других не обдуманых реализаций.
                                                    Хотите bcrypt с перцем, хорошо — тогда сделаете хотя бы так:
                                                    $hash = password_hash(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes, PASSWORD_BCRYPT)
                                                    

                                                    Здесь по крайней мере всегда хэшим 72 байта, sha2 отработает всю последовательность глобального ключа, а bcrypt «обеспечит» медленный перебор.
                                                    Хотя считаю свое предложение ниже все же лучше, т.к. двойная упаковка в криптоанализе все-таки не приветствуется (влияние прехэширования на окончательное хэширование бикриптом тяжело прогнозируемо — попробуйте на досуге дать вероятностную характеристику рискам — где-то после 10-й страницы формул желание продолжать отпадет однозначно).
                                                      0
                                                      Вы сами предложили перепаковку и сами её раскритиковали.
                                                      Я против этой конкретной и других не обдуманых реализаций.
                                                      Они обдуманы, я же нигде не говорил, что обрезать до 72 байт категорически ненужно, я говорил о том что даже в кривом варианте, при смене апи (если он сам перестанет обрезать до 72 байт, это можно будет легко поправить — обрезав самостоятельно.
                                                        +1
                                                        Нет, вы кое-что пропустили
                                                        sha2 отработает всю последовательность глобального ключа
                                                        .
                                                        Каждый бит сольет с паролем. У вас же, оно его просто обрежет.

                                                        Вам расписать мой алгоритм?
                                                        Хэшируем всю последовательность sha512 (блоками по 128 байт), результат в raw виде (512 бит / 64 байт) добиваем до 72 байт (просто, чтобы юзать бикрипт на всю ширину) и хэшируем уже окончательно.

                                                        И как минимум, я бы покрыл его хотя бы минимальным набором тест-кейсов (я понимаю, что тест-кейсы не для таких крутых ребят как мы — но это все же безопасность):
                                                        <?php
                                                        $pepper_very_long = 'very very long pepper - 56448s785o7856pv85c jh 78o78 8po5c8 7töl öl7öl87ö7  p78t896cüü üä69ä öpö 9ö6 ö öö ö ö 76876 876 lk lg77 7p li5xlu87xo764o7o476x4kl76kl';
                                                        $pepper_8_bytes = '01234567';
                                                        $pwds = array(
                                                        'abc', 
                                                        'abcd', 
                                                        'very very long password very very long password very very long password',
                                                        'very very long password very very long password very very long password2'
                                                        );
                                                        
                                                        foreach ($pwds as $pwd) {
                                                        
                                                        $len = strlen(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes);
                                                        if ($len != 72) {
                                                          throw new Exception("Invalid length for bcrypt \"$len\" ...");
                                                        };
                                                        $hash = password_hash(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes, PASSWORD_BCRYPT);
                                                        
                                                        ?>
                                                        
                                                        <b><?= $pwd ?></b> : <br/> 
                                                         <?= $hash ?><br/>
                                                        
                                                          positive: <?= password_verify(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes, $hash) 
                                                          ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                        <?php $pwd = ''; ?>
                                                          negative: <?= (!password_verify(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes, $hash)) 
                                                          ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                        <br/>
                                                        
                                                        <?php } ?>

                                                          0
                                                          Где это я упустил?
                                                          Используете алгоритм пробегающийся по всей последовательности — резать не нужно, используете те что ограничивают длину нужно резать.
                                                          Мы же говорили конкретно о применении перца, суть которого не меняется что в sha512, что в bcrypt.
                                                          Повторяю ещё раз: перец никоим образом не меняет алгоритм криптографии, просто даёт дополнительную защиту от случаев когда хэш пароля УЖЕ расшифровали.
                                                        +1
                                                        > hash('sha512', ($pwd. $pepper_very_long), 1)

                                                        Блиииннн… Люди, ну откройте для себя HMAC, наконец. Хватит склеивать.
                                            +6
                                            Кстати совет «копипастерам» (в принципе вверху правильно процитировали про «мильен способов»):
                                            Если не понимаете ни черта ни в криптографии, и даже лень заглянуть в доку, не используйте «перец» — используйте стандартную реализацию как оно есть.

                                            Просто потом зашифруйте хэш (и соль если есть) каким-нибудь секретным ключом (в качестве «перца») и начальным вектором, который постояннен для пользователя например id (в качестве «приправы») любым симметричным алгоритмом, хоть тем же aes.

                                            Я вам клянусь — вы ничего не сможете испортить, более того все «нехорошие» пункты про «перец», которые ув. автор помянул в статье для этого способа не справедливы:

                                            Проблема инвалидации — Если Вы захотите его сменить, например, в случае утечки
                                            то Вам нужно просто перешифровать все хэши (и соли) новым ключем.
                                            Проблема хранения
                                            эту проблему придумал автор — ее нет. Он вероятно пока из тех, кто хранит все деньги в одном банке.
                                            Проблема «иллюзии защищенности»
                                            этой проблемы тоже не существует.
                                            Проблема реализации — Не существует ни одной академической работы или RFC...
                                            Тут автор сам себя переплюнул: а ничего что тоже самое можно про bcrypt сказать? Но многие и автор статьи в том числе его используют…
                                              –2
                                              Вы это мне ответили? Относитесь к перцу не как к чем-то меняющеему алгоритм, а как к добавлению пароля самого пароля.
                                              <?php
                                              echo password_verify($password. $password, $hash) ? 'Login OK' : 'Wrong password';
                                              
                                                0
                                                Нет, это было просто кстати. Сорри за хиерархию…
                                                +4
                                                Если не понимаете ни черта ни в криптографии...

                                                Просто потом зашифруйте хэш (и соль если есть) каким-нибудь секретным ключом (в качестве «перца») и начальным вектором, который постояннен для пользователя например id (в качестве «приправы») любым симметричным алгоритмом, хоть тем же aes.

                                                … начальным вектором...

                                                … любым симметричным алгоритмом...

                                                … тем же aes.

                                                Ни черта не понимающие в криптографии люди уже в ужасе.
                                                  0
                                                  Сорри, исправляюсь. Под спойлером небольшой код для пхпистов, ни черта… ну вы поняли:
                                                  Код
                                                  <?php
                                                  $pepper_very_long = 'very very long pepper ...';
                                                  
                                                  // -----------------
                                                  
                                                  function pepper_encode( $hash, $pepper_very_long, $pepper_cor_user, $mode ) {
                                                  
                                                  $td = mcrypt_module_open ('rijndael-256', '', 'ofb', '');
                                                  
                                                  // pepper initial vector:
                                                  $pepper_iv = substr( hash('sha512', $pepper_cor_user, 1) , 0, mcrypt_enc_get_iv_size($td) );
                                                  // pepper key:
                                                  $pepper_key = substr( hash('sha512', ($pepper_cor_user . $pepper_very_long), 1) , 0, mcrypt_enc_get_key_size($td) );
                                                  
                                                  // intialize encryption ... and encrypt :
                                                  mcrypt_generic_init($td, $pepper_key, $pepper_iv);
                                                  
                                                  // encrypt or decrypt (if hash is a encrypted, cause we are symetrical) :
                                                  switch ($mode) {
                                                  	case 'encrypt' :
                                                      $encrypted = base64_encode(mcrypt_generic($td, $hash));
                                                    break;
                                                  	case 'decrypt' :
                                                      $encrypted = mdecrypt_generic($td, base64_decode($hash));
                                                    break;
                                                    default:
                                                      throw new Exception("Error ...", 1);
                                                  }
                                                  
                                                  // free handles and close module ...
                                                  mcrypt_generic_deinit($td);
                                                  mcrypt_module_close($td);
                                                  
                                                  return $encrypted;
                                                  
                                                  }
                                                  
                                                  // ---------------
                                                  
                                                  $userid = 555;
                                                  $pepper_vector = "vector: $userid / $userid";
                                                  
                                                  $pwds = array(
                                                  'abc', 
                                                  'abcd', 
                                                  'very very long password very very long password very very long password',
                                                  'very very long password very very long password very very long password2'
                                                  );
                                                  
                                                  foreach ($pwds as $pwd) {
                                                  
                                                  // bcrypt - we have hash...
                                                  $hash = password_hash( $pwd , PASSWORD_BCRYPT );
                                                  
                                                  // encrypt it for database :
                                                  $encr_hash_4_db = pepper_encode($hash, $pepper_very_long, $pepper_vector, 'encrypt');
                                                  
                                                  ?>
                                                  
                                                    <b><?= $pwd ?></b> : <br/> 
                                                    bcrypt: <?= $hash ?> <br/> with pepper 4 DB: <?= $encr_hash_4_db ?> <br/>
                                                  
                                                  <?php
                                                  
                                                  // decode with pepper :
                                                  $decr_hash = pepper_encode($encr_hash_4_db, $pepper_very_long, $pepper_vector, 'decrypt');
                                                  
                                                  // test cases :
                                                  ?>
                                                    decrypt: <?= $decr_hash ?> <br/>
                                                  
                                                    positive: <?= password_verify( $pwd, $decr_hash ) 
                                                    ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                    negative1: <?= ( !( 	password_verify( ($pwd.'-') , $decr_hash )  ) )
                                                    ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                    negative2: <?= ( !( password_verify( ('-'.$pwd) , $decr_hash ) ) )
                                                    ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                    negative3: <?= ( !( password_verify( '' , $decr_hash ) ) )
                                                    ? '<b style=color:green>OK</b>' : '<b style=color:red>BOOM</b>' ?>, 
                                                  <br/>-----------------<br/>
                                                  
                                                  <?php } ?>
                                                  

                                                  Выдает примерно следующее...
                                                  abc:
                                                  bcrypt: $2y$10$SYASLWPtaVeM7jaUlxHJHOZtkrxIHqeVR9negP29nAn07JXYHffIG
                                                  with pepper 4 DB: /WiJ6LmPROZd8/+cJJ+snwFqnyz51maCLp2lYuDWEtfTjB66GiechlC6TDIEhxutC1LrURGz8mCOz2Zi
                                                  decrypt: $2y$10$SYASLWPtaVeM7jaUlxHJHOZtkrxIHqeVR9negP29nAn07JXYHffIG
                                                  positive: OK,
                                                  negative1: OK,
                                                  negative2: OK,
                                                  negative3: OK,

                                                  abcd:
                                                  bcrypt: $2y$10$oJ2.2p4U4Du6xCmPANppLuLaCuRy5qdE9OPe4L2siPjQRIAdAdSU6
                                                  with pepper 4 DB: /WiJ6LmPRNpOgILiA/uNyhN65GPQ2mOvGKWfZtrAB//Upi7HGiaP7SaETGEYh1GqGlaKNBKqz2mM+noT
                                                  decrypt: $2y$10$oJ2.2p4U4Du6xCmPANppLuLaCuRy5qdE9OPe4L2siPjQRIAdAdSU6
                                                  positive: OK,
                                                  negative1: OK,
                                                  negative2: OK,
                                                  negative3: OK,

                                                  very very long password very very long password very very long password:
                                                  bcrypt: $2y$10$22zG.6xW2Q2.BKPtIs3hk.t4S8/mMwtqjMMsVMxqbG1Uw3E8wce8i
                                                  with pepper 4 DB: /WiJ6LmPRIc2yOv+RbePzAY9/FnY50enJeaHQYH4Uu+Z2zq/HDa7viSZWgMZzVOhDQ2OEWiuk1+LzBdM
                                                  decrypt: $2y$10$22zG.6xW2Q2.BKPtIs3hk.t4S8/mMwtqjMMsVMxqbG1Uw3E8wce8i
                                                  positive: OK,
                                                  negative1: OK,
                                                  negative2: OK,
                                                  negative3: OK,

                                                  very very long password very very long password very very long password2:
                                                  bcrypt: $2y$10$BAne9OplDsklY3oriwtqo.HrUkn3a12mWMCYGpU4ousB7oossVSmy
                                                  with pepper 4 DB: /WiJ6LmPRPdF3MnpPL+0uiRkvkKg2EGHIaGeRYHEFOnKmmSTWnCngySXcBIk4BasP0+ZUTSE2Fu++kJc
                                                  decrypt: $2y$10$BAne9OplDsklY3oriwtqo.HrUkn3a12mWMCYGpU4ousB7oossVSmy
                                                  positive: OK,
                                                  negative1: BOOM,
                                                  negative2: OK,
                                                  negative3: OK,


                                                  Последний BOOM специально, потому что пользователь имеет пароль длиннее 72 char и 73-й роли уже не играет. Не хотел сдесь снова холиварить на эту тему — просто заостряю внимание (эти 72 символа тоже нужно еще подобрать).
                                                  «Перчим» и «разперчиваем» отдельно… (например перед сохранением в базу и при чтении из нее). base64_encode, base64_decode просто для наглядности, в базу можно писать и raw данные (в поле соответствующего типа ессно).
                                                  0
                                                  Ой, а можно пример кода по пункту: «то Вам нужно просто перешифровать все хэши (и соли) новым ключем.»? Я, например, не знаю, как решить эту задачу.
                                                  Дано: БД с хэшами паролей, корректно зашифрованных солью+перцем1.
                                                  Найти: БД с хэшами тех же самых пролей, корректно зашифрованных солью+перцем2.
                                                    0
                                                    Видимо имеется ввиду перехэширование в момент авторизации пользователей, как сказано habr.com/post/194972, функция password_needs_rehash. Но это не решение, или не самое лучшее решение, которое приведёт к тому, что в БД постоянно будут храниться хэши двух и более типов, которые к тому же надо поддерживать в коде, как итог путаница.
                                                      0

                                                      нет, имелось ввиду совершенно другое (см ниже)

                                                      0

                                                      Боюсь с вашим дано-найти то не очень работает (если "перец" не есть ключ симметричного шифрования).


                                                      Выше я имел ввиду что посоленные пароли (хеши) перед записью в базу шифруются симметричным алгоритмом (например AES) единственным секретным ключом (aka "k1"), не хранящимся в базе.
                                                      Тогда процедура перешифрования на k2 при возможной компрометации k1 выглядит след. образом:


                                                      for usrid, pwdhash in get_all_usr_pwd():
                                                        pwdhash = AES.decrypt(pwdhash, k1);
                                                        pwdhash = AES.encrypt(pwdhash, k2)
                                                        write_usr_pwd(usrid, pwdhash)

                                                      Если вы подмешали ваш "перец" в процесс генерации хеша (и этот процесс не симметричный), то тогда перешифрование возможно только в процессе авторизации для каждого пользования отдельно.

                                                        –1
                                                        Ну вообще-то в статье шла речь о конкретном bcrypt, насколько я понимаю, не симметричном алгоритме, поэтому прошу признать актуальность «Проблема инвалидации». По остальным контраргументам проблем «перца» ваши доводы заслуживают внимания.
                                            +2
                                            Не все читают документацию От и До.
                                            Статья носит предупреждающий характер, а не конкретно, что так делать нельзя и это единственный путь.
                                              0
                                              Посыл статьи как-раз понятен и даже способ донести в чем-то немного импонирует… Непонятна реакция на нее, и ваша кстати тоже.
                                              Не все читают документацию От и До.
                                              Вот это-то как раз и плохо, а в случае с security еще и безответственно.

                                              Дело не в том, что ее «не все читают», дело в том, что те кто не читают и соответственно ни черта не смыслят в материи, тем не менее имеют свое, подчас авторитарное, мнение.
                                            +2
                                            Ждём на кулинарных сайтах эту статью, попавшую под критерии парсеров :)
                                              +2
                                              Пространный философский вопрос, почти анриельтед: зачем bcrypt если есть PKCS#5?
                                                +8
                                                Зачем использовать bcrypt и PBKDF2 (aka PKCS#5) если есть scrypt?
                                                image
                                                  +2
                                                  А как так, 40 символов MD5 дешевле расшифровки 10 символов MD5? Опечатка? Или это какая-то особенность?
                                                    +3
                                                    «text» — это alphanumerics, какая понимаю. «Chars» — это что угодно из 0..255.
                                                      0
                                                      Спасибо, и вправду, не заметил: во всех строках так.
                                                        0
                                                        Даже если считать каждую букву текста принимающей значения из 16 разных символов, две таких буквы идентичны одному байту, принимающему 256 значений, поэтому, вероятно, в таблице используется текст в смысле осмысленного литературного текста, состоящего из достаточно ограниченного числа слов, а не просто alphanumerics.
                                                        0
                                                        Нет текст — это текст (т.е. при переборе буквы связываются на английский манер, короче не ерунда какая-нибудь, а реально текст — естественно вариантов меньше);
                                                        For each key derivation function, we consider six different types of password:
                                                        — A random sequence of 6 lower-case letters; e.g., “sfgroy”.
                                                        — A random sequence of 8 lower-case letters; e.g., “ksuvnwyf”.
                                                        — A random sequence of 8 characters selected from the 95 printable 7-bit ASCII characters; e.g., “6,uh3y[a”.
                                                        — A random sequence of 10 characters selected from the 95 printable 7-bit ASCII characters; e.g., “H.*W8Jz&r3”.
                                                        — A 40-character string of text; e.g., “This is a 40-character string of English”.
                                                        — An 80-character string of text; e.g., “This is an 80-character phrase which you probably won’t be able to crack easily.”.
                                                        0
                                                        Зачем использовать bcrypt и PBKDF2 (aka PKCS#5) если есть scrypt?


                                                        Наверно затем, что scrypt при рекомендованных параметрах отедает до 16Mb оперативы на одно вычисление, bcrypt в этом плане менее прожорлива.
                                                          +1
                                                          Это как раз преимущество scrypt
                                                            0
                                                            Разве? Злоумышленнику-то памяти всегда хватит, а на веб-сервере ресурсы ценнее, особенно ежели он на VDS.
                                                              +3
                                                              Злоумышленник по определению пытается параллелиться. И параллелиться очень помногу на современных видеокартах. И вот тут масштаб параллелилзации грубо пресекается объемом необходимой памяти.

                                                              Если веб-сервер работает на VDS, то, скорее всего, такие требования к безопасности паролей на нем лишние.
                                                                –1
                                                                Память всегда можно увеличить. Чтобы не параллелилось нужно, чтобы результаты одного вычисления были зависимы от другого.
                                                                К тому же, что касается PBKDF2 то не указано, какая именно там хэш-функция указана, её ведь можно менять.
                                                                  +2
                                                                  На том scrypt, в частности, основан. Плюс потребление памяти. Чем более ресурсоемко (неоптимизируемо ресурсоемко) вычисление хеша, тем труднее его сломать брутфорсом.
                                                                    –1
                                                                    >Чтобы не параллелилось нужно, чтобы результаты одного вычисления были зависимы от другого.
                                                                    Это не защитит от параллельного перебора разных паролей.
                                                                0
                                                                Разве что для безопасника. Такие требования подымают стоимость и железа и разработки. Причем с размером аудитории бурлеск неадекватных трат будет также расти.
                                                                  +1
                                                                  Пользователь аутентифицируется в системе сравнительно редко, так что такого уж бурного роста пожирания ресурсов из-за алгоритма хеширования, скорее всего, не случится. С железом — понятно, VDS уже не хватит, но на большом проекте уже можно и на приличную железку раскошелиться. А вот чем это поднимет стоимость разработки, мне непонятно.
                                                                    0
                                                                    Редко — это на сайте Васи Пупкина (на хрена ему вообще сложности с безопасностью, если ценность сайта мизер). Все зависит от профиля системы. К примеру банковские сотрудники на работу приходят в определенное время и всплеск логинов будет в течение короткого интервала. Другой вариант — гигантская аудитория. Логины там будут происходить постоянно. С такими требованиями к ресурсам это будет одна из наиболее тяжелых операций.

                                                                    Сложность разработки начнется в тот момент, когда окажется недостаточным одной железки и надо начинать клепать кластерное решение. Чем дальше, тем сложнее система будет. Разработка и поддержка соответственно тоже.
                                                                      0
                                                                      Скорость работы scrypt вполне удовлетворительна — порядка сотен логинов в секунду. Потребление памяти ровно на это время. Достаточно для аутентифицирования легальных пользователей, плохо для брутфорса.

                                                                      Если у Вас ценность сайти и информации на нем высока, Вы раскошелитесь на дорогое и надежное решение, верно?
                                                                        0
                                                                        Скорость работы scrypt вполне удовлетворительна — порядка сотен логинов в секунду.

                                                                        Булшит Бинго! Инженер не может дать оценку системы без наличия системы.

                                                                        1ый акт,
                                                                        Уже которую неделю сыпятся посты о безопасности веба. Я не берусь оценивать авторов и их квалификацию, это не моя тема. Но общий результат излияний ну чуть больше нуля.
                                                                        У меня сейчас стоит задача разрабтать фронт с защитой >= «средний лвл кулхацкера». Читаю поголовно все топики, пробовал спрашивать. До сих пор я не увидел описания шаблонов по применению или лучших практик. Максимум из полезного выудил — две ссылки и пара комментариев. Один сайт достаточно велик и мне приходится тратить много времени на изучение. По 2ой ссылке документ с описанием как надо и как не надо делать хранение пароля. (Но к примеру есть расхождение в порядке расположения соли и пароля. На хабре поголовно советуют соль помещать в конце. Документ не придавал этому значения. Судя по этому топику проблема лишь в говнокоде реализации конкретных функций конкретного языка.)
                                                                        У любого разработчика есть вагон и маленькая тележка технологий, с которыми надо иметь дело и которые меняются довольно интенсивно. Невозможно объять необъятное, поэтому существуют специализации. Каждый делает свое дело. Почему все безопасники ожидают от прочих понимания на уровне не меньше среднего?
                                                                        Дайте уже нормально описанный процесс как надо делать. От кого еще его ждать?!

                                                                        2ой акт.
                                                                        Разработка — это не раз, два и готово (нормальная разработка).
                                                                        Всунуть заведомо медленный алгоритм с гигантским потреблением по памяти… Ну если в системе 2 человека, то конечно пофиг. Пойдем от вашей цифры в 100 пользователей в секунду. Примерно подходит для системы с посещаемостью в несколько миллионов в сутки. В мире их не так уж и мало. Ценность аудитории очевидно высока. Учитывая длительность и тяжесть операции шифрования, вес процесса входа в систему возрастает. Вам точно придется раскошелиться на процы и память (хотя с ней даже пофиг, относительно процов). Не дай бог у вас еще вендор-лок с софтом (многие считают лицензии по ядрам). Чем выше уровень кластеризации, тем больше граблей вы огребете и в разработке и в сопровождении. Чтобы все эти затраты окупились, предприятие должно обладать высокой доходностью и большой зависимостью от целостности данных клиентов. Т.е. нужен адекватный анализ критериев разработки системы. Пихать везде подряд — бред.
                                                                          0
                                                                          Я не спорю с тем, что такой подход требует анализа. Сайт Василия Пупкина вполне обойдется и md5 :)
                                                              +1
                                                              Затем что PBKDF2 это стандарт в отличии от scrypt.
                                                                +1
                                                                scrypt основан на PBKDF2 и использует его, его защищенность как минимум не меньшая, чем PBKDF2. Правда, это и не значит, что превосходит :) Вы правы в том, что PBKDF2 оттестирован качественнее, чем bcrypt и scrypt
                                                                  +1
                                                                  Если криптовелосипед использует какой-то оттестированный стандарт — совершенно не значит что он имеет такую же или лучшую защищенность. Пример: mac-and-encrypt, использует два криптопримитива MAC и блочный или потоковый шифр, однако из-за неправильного применения MAC через него может утекать информация о исходном сообщении. Особенно если учесть что RFC-драфт на scrypt истек почти год назад (пруф: tools.ietf.org/html/draft-josefsson-scrypt-kdf-01).
                                                                  Но вообще надо отметить что идея scrypt мне нравится.
                                                            +8
                                                            И все-таки, перец нужен. Он защищает от отдельного класса атак — атак вида «глупый админ забыл зашифровать бэкапы базы и положил их в открытый доступ». В отличие от соли, которая не поможет при слабом пароле, перец в принципе не даст злоумышленнику начать подбирать пароли, пока он не получит еще и бэкапов сайта.

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

                                                            PS да, и шифрование — хорошая альтернатива перцу.
                                                              +1
                                                              Напрашивается вопрос — а какую реализацию перца вы можете предложить, что бы если что, его можно было бы сменить?
                                                                +3
                                                                Хранить список перцев, а в БД — номер использованного. При хешировании пароля для записи его в БД всегда используем последний перец. При логине пользователя его пароль перехешируется, если использованный перец устарел.
                                                                  0
                                                                  Мм… То есть года через три у нас будет пятнадцать устаревших перцев, один «свежий» и двести пятьдесят тысяч паролей, хешированных вразнобой? Чем это безопаснее одного перца, при условии што подобрать перец малореально, а украсть из хранилища што один, што шестнадцать одинаково сложно/просто?
                                                                    0
                                                                    Все активные пользователи всегда будут иметь пароли, хешированиые со свежим перцем. Вразнобой окажутся хешированы только неактивные записи.
                                                                      0
                                                                      Это, как ни странно, не отвечает на мой вопрос.
                                                                –7
                                                                Все-таки требования к стойкости пароля гораздо сильнее повышают стойкость системы, чем введение перца.
                                                                Представьте, что злоумышленник — «глупый помощник админа», у которого нет прав на запись, но есть права на чтение.
                                                                  +9
                                                                  Любые попытки заставить пользователя использовать стойкий пароль приводят к тому, что пользователь начинает хранить его открытым текстом в доступных местах.

                                                                  Лично у меня есть один совершенно нестойкий пароль, который я придумал еще 10 лет назад и с тех пор использую его на всех тех сайтах, которые вымогают регистрацию. А второй нестойкий пароль я использую во всех онлайн игрушках. Почему? Да потому что воровать там у меня нечего.
                                                                    –2
                                                                    Удивительно. Почему-то у среднего хабраюзера прямо-таки аллергия на упоминание стойкости паролей.
                                                                    Это тем более странно, что если авторы предыдущей статьи хотя бы предлагали альтернативу — тот самый перец — то здесь-то и вовсе крыть нечем. То есть, пароли вида 12345 и vasya — это неизбежный фейл.
                                                                    Фейл, который сведет на нет любые ухищрения с хешированием. Это в буквальном смысле навешивание бронированной двери на картонный сарай.

                                                                    10 000 самых распространённых паролей будут перепробованы за считанные секунды. А это много. Все ваши любимые имена-отчества включая ласкательно-уменьшительные, наряду с датами рождения плюс весь ваш словарный запас в английском языке.
                                                                    Пароль длиной в 6 символов, состоящий из одних латинских букв, будет взломан за считанные часы. И никакая соль, никакой алгоритм не помогут.

                                                                    Заявления «у меня нестойкий пароль потому что воровать нечего» совершенно смехотворны в контексте обсуждаемого вопроса. Если вас не волнует безопасность пароля, то уж тем более проблема перцев и хэшей не должна волновать и подавно. Но почему-то обе статьи на эту тему вызвали довольно большой ажиотаж. Неувязочка получается.
                                                                      0
                                                                      Сайт должен обеспечить безопасность для тех пользователей, кому она нужна.
                                                                      Сайт не должен обеспечивать безопасность тем пользователям, кому она нафиг не сдалась.
                                                                      Где здесь противоречие?
                                                                        –2
                                                                        Противоречий тут очень много.
                                                                        Начиная с того, что комментаторы при упоминании сложного пароля тут же начинают воображать себе злой сайт с дубинкой. И начинают спорить с этим воображаемым сайтом. При этом минусуя комментарий, в котором не было ни слова ни про сайт, ни про дубинку.

                                                                        «Сайтами, которые вымогают регистрацию» интернет не ограничивается. Рассказывать про одни сайты и умалчивать в своем комментарии о других — это или шулерство или глупость.

                                                                        Это уже вторая статья, которая претендует на роль всеобъемлющего руководства по хэшированию. И в ней тоже не упоминается такой важный элемент, как стойкость пароля.

                                                                        «Сайт не должен обеспечивать безопасность тем пользователям, кому она нафиг не сдалась» — это тоже очень смешное высказывание. Для аудитории хабра вполне годится, но для дискуссии о безопасности, увы — нет.
                                                                    +1
                                                                    Против «глупый админ забыл» вообще нет приема защиты.
                                                                      +1
                                                                      Есть. Просто надо построить модель админа — которая опишет, какие вещи он забывает чаще, какие — реже.

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

                                                                      Иногда, в процессе работы над конкретными проектами, модель админа уточняется. Так, построенная нашей командой модель школьного админа включает в себя тот факт, что они никогда не следят за точностью часов на сервере — расхождение может быть как на несколько минут — так и на несколько лет, часовой пояс также может быть выставлен произвольно. Ну и сервер без батарейки — норма.
                                                                        +1
                                                                        Но принцип Керкгофса позволяет предположить, что исходники Вашей системы не менее доступны, чем содержимое БД. С большой вероятностью они известны как минимум разработчикам, и увольнению любого из них сразу компрометирует систему. Неужели Вы заставите пользователей менять пароли каждый раз, когда происходит замена в коменде разработчиков?
                                                                          0
                                                                          Во-первых, принцип Керкгофса говорит о доступности алгоритма, а не исходных кодов. То есть вполне допускается наличие в исходнике «тайных» мест — при условии, что они не имеют ни малейшего отношения к алгоритму.

                                                                          Во-вторых, перец можно хранить и в файле конфигурациию

                                                                          В третьих, далеко не каждое увольнение разработчика компрометирует систему. На самом деле, я бы больше опасался принятия нового разработчика в команду :)

                                                                          Ну, и в четвертых, прочитайте, пожалуйста, ветку выше, посвященную смене перца без смены паролей всеми пользователями.
                                                                            0
                                                                            Файл конфигурации тоже кому-то доступен. Например, админу системы :) Увольнение админа — компрометация.

                                                                            Ветку я видел, но мне кажется, что увод паролей неактивных пользователей — тоже вполне себе бедствие.
                                                                              0
                                                                              В любом случае, хуже от добавления перца не становится (если, конечно, не косячить так, как описано в статье). Система с перцем и солью защищена лучше, чем система просто с солью.
                                                                                +2
                                                                                Профит от такой «защиты» совершенно минимальный. Ну да, стальная дверь с системой засовов, подпертая тростинкой, защищена лучше, чем просто стальная дверь с системой засовов, я тут даже спорить не буду.
                                                                    +5
                                                                    Задачи «соли» не увеличить криптостойкость, а защититься от rainbow таблиц и не более. Даже тупой
                                                                    md5(md5($password).$salt) уже защищает от rainbow таблиц, особенно если $salt рандомная для каждого пользователя, генерировать под каждый вариант rainbow таблицу слишком накладно.
                                                                      +4
                                                                      > md5(md5($password).$salt)

                                                                      Великолепный пример криптовелосипеда, кстати :-)
                                                                      Зачем Вы хешируется пароль, перед тем как как хешировать его с солью(md5($password))?
                                                                        +9
                                                                        На самом деле, так сложилось исторически. Сначала все веб-программисты использовали md5($password), но потом им кто-то рассказал про соль. Но как посолить уже существующую базу? Так и появилась функция md5(md5($password).$salt)
                                                                      +2
                                                                      А не лучше ли для предотвращения атаки по утёкшей базе использовать вместо этого перца обратимое шифрование хешей в базе?
                                                                      Степень защиты та же: пока не утекли исходники, не утекли и пароли.
                                                                      Зато есть возможность автоматически перешифровать все хеши в базе при компрометации исходников.

                                                                      PS. Более того: для обратимого шифрования можно использовать, к примеру, встроенные в MySQL AES функции, и пароль хранить в переменной MySQL. Тогда ни утёкшая база, ни утёкшие исходники не дадут доступ к пользовательским паролям.
                                                                        0
                                                                        А если база утекла, не значит ли это что с большой вероятностью утекла и эта самая переменная?

                                                                        Я полагаю, что основной причиной утечки БД является инъекция, в результате которой злоумышленник получает доступ к таблицам и переменным на тех, же правах, что и скрипт аутентификации.
                                                                        И реже это следствие утечки дампов, впрочем, в которых так же может быть сохранена эта переменная.
                                                                          0
                                                                          Переменные в дамп не попадают, насколько я помню. Водить её можно SQL запросом из shell-скрипта при загрузке сервера.
                                                                            0
                                                                            Ну положите в мемкеш. Тогда злоумышленнику потребуется еще и выполненние произвольного кода(или уязвимость с доступом к кешу). С локальным параметром есть куда фантазии разгуляться. Вопрос соотношения целесообразности/производительности
                                                                          +1
                                                                          В той статье упоминается что нужно добавлять всегда соль и перец слева от пароля.
                                                                          А статья ваша начата изначально что вы с самого начала неправильно следуете советам и обвиняете что они не верны
                                                                            0
                                                                            Что-что? Где же неправильное следование советам?
                                                                            Перед добавляется слева, как и говорилось в той статье.

                                                                            Или вы про отсутствие соли? Ну так добавьте ее тоже — не поменяется ровным счетом ничего.
                                                                              0
                                                                              Утро + топологический кретинизм

                                                                              Цитата из статьи.
                                                                              Чтобы усложнить жизнь при атаке перебора следует дописать соль к паролю, а не наоборот (для людей, которые пишут слева направо, конечно).


                                                                              следовательно
                                                                              $password. $salt

                                                                              А здесь почему то наоборот считают
                                                                                0
                                                                                Хм, моя ошибка. И правда, в статье советуют добавлять соли с перцем справа.

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

                                                                                Легко видеть, что при таком решении ситуация, описанная в этой статье, повторится.
                                                                                  0
                                                                                  скорее всего, и что самое печально далеко не все читают описания функций на php.net перед применением
                                                                            +2
                                                                            Обновил пост
                                                                              +3
                                                                              Как раз хотел написать статью про практическую реализацию с примерами ошибок реализации хеширования.
                                                                              Там и этот пример и еще более классический с DES и 8 байт в .htpasswd и другие.

                                                                              Спасибо!

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

                                                                              Тем не менее, вы сделали полезное дело, что написали людям про свои ошибки. Спасибо!

                                                                              «Перец, как бы «усиливает» слабый пароль, но только в недрах системы. Через форму логина пароль «asdf» подбирается все так же легко. Слабый пароль — это слабый пароль, как бы Вы себя не обманывали.»

                                                                              Именно так. Это называется онлайн и офлайн атаки. И от онлайн атаки есть другие методы, такие как freeze. В статье же были рассмотрены только офлайн атаки, о чем написано отдельно. Но вы просто решили хаить, вам же все равно :)
                                                                                +5
                                                                                Как раз хотел написать статью про практическую реализацию с примерами ошибок реализации хеширования.

                                                                                Обязательно пишите.

                                                                                Тем не менее, вы сделали полезное дело, что написали людям про свои ошибки. Спасибо!

                                                                                Я, слава мозгу, такой ошибки не допустил. Это просто демонстрация.

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

                                                                                Если дописывать перец с другой стороны — все будет работать.
                                                                                Проблема в том, что если бы я делал все, как в моей статье, но использовал бы более короткий перец, то все тоже бы работало!
                                                                                Работает != правильно работает.
                                                                                «Работает» — это не показатель в криптографии. Проблема именно в этом.
                                                                                Нужны исследования. Не битва в комментариях, не апелляции к логичности, а исследования. Серьезные, научные.
                                                                                  +1
                                                                                  А почему бы попросту не ксорить пароль солью перцем (хотя я считаю, что перец — мера бесполезная)? Тогда вопрос «куда добавлять приправы — в начало или конец» отпадет сам собой.
                                                                                    0
                                                                                    Тогда можно поменять пароль пользователю без смены хеша… Не знаю, чем это грозит, но без специальных исследований я так делать не рискну (вспомните про уязвимость в Телеграмме — а ведь там тоже «всего лишь» поксорили). Если же такие исследования уже были — поделитесь ссылкой, пожалуйста.
                                                                                      0
                                                                                      Это как можно поменять? :) Если у Вас хеш пароля представляет из себя конкатенацию соль + разделитель + хеш-функция(соль циклический_xor пароль), как тут заменить пароль, не меняя хеша? И уязвимость в Телеграмме не имеет никакого отношения к данному способу «соления».
                                                                                        0
                                                                                        Запросто. Чтобы сменить пароль1 на пароль2, я просто заменю соль на соль циклический_xor пароль1 циклический_xor пароль2
                                                                                          0
                                                                                          Соль является составной частью хеша пароля. :) Теперь Ваша задача — подменить соль (и, как следствие, хеш пароля на сервера). Если Вы это можете сделать, с тем же успехом можете подменить хеш, сделанный конкатенацией. Кроме того, в Вашей формуле есть пароль1. Если Вы его уже знаете, то заменить пароли можно штатными средствами. :)
                                                                                            –3
                                                                                            Вы меня не слышите… Хеш пароля не меняется. После изменения соли он остается корректным, но уже для другой пары «новый пароль — новая соль». А старый пароль знать не надо — достаточно знать циклический xor старого и нового паролей…
                                                                                              0
                                                                                              Как Вы получите циклический xor с новым паролем, не зная старого пароля? Если у Вас есть новый пароль и его xor со старым паролем, Вам не составит труда получить старый пароль, ничего не меняя напрямую. :)

                                                                                              Но — самое главное — как Вы измените соль, не меняя ничего в БД? :) Если Вы можете изменить соль в БД, Вы с тем же успехом можете изменить и сам пароль.
                                                                                                0
                                                                                                Зная новый пароль и его xor со старым, восстановить старый — как два байта переслать
                                                                                      –1
                                                                                      отдельно написано про необходимость первым передавать в подхешевое выражение пароль, а вы сделали наоборот :)
                                                                                      А если не bcrypt, а другая реализация? Которая «режет» сначала?

                                                                                      А что, если не режет, а просто ослабляет хэш (если пароль стоит в начале), например исходя из того, что длинна пароля часто много меньше глобального ключа, при некоторых условиях и естественно «слитом» глобальном ключе, находится такая возможность предсказать, что например если на итерации 10 xor маска 2 и 5 бита по блоку равна нулю, можно остановить расчет хэша (еще 100 итераций), т.к. он уже не сойдется. Это как минимум ускоряет брут.

                                                                                      При прочих равных, при последовательном хэшировании «правильное» соление (не важно глобальным ли «перцем» или пользовательской ли солью) лучше производить как раз сначала. Нет, не совсем правильно: лучше неизвестные взломшику биты в хэшируемой последовательности (именно пароль а не соль) расположить ближе к концу, т.к. они (биты) как раз оказывают наиболее сильное воздействие на результат хэширования.
                                                                                      В общем случае, не вникая в общую схему хэширования (что не есть хорошо), исходя из того, что все сделано правильно, все же можно однозначно сказать, что вероятностные характеристики у следующих двух алгоритмов разные, и у первого все-таки лучше чем у второго (повторяю — $pwd не известен, $hash и $pepper слили):
                                                                                      $hash = password_hash(hash('sha512', ($pepper_very_long . $pwd), 1) . $pepper_8_bytes, PASSWORD_BCRYPT)
                                                                                      
                                                                                      $hash = password_hash(hash('sha512', ($pwd . $pepper_very_long), 1) . $pepper_8_bytes, PASSWORD_BCRYPT)
                                                                                      

                                                                                      Это математически доказуемо кстати (взять хоть тот же birthday problem за основу и немного распотрошить оценку вероятности коллизии SHA512). Bcript конечно очень снижает влияние этих рисков (т.к. много медленнее SHA), но это сегодня, завтра все может быть с точностью наоборот. Кроме того цель ведь усилить «перцем» алгоритм, а не ослабить его.

                                                                                      Такая вероятностная характеристика для SHA2 справедлива только при условии, что длинна $pwd + $pepper_very_long больше размера хэшируемого блока (1024 для SHA2-512). Если размер этой последовательности короче размера блока (т.е. хэшируем уже не последовательно, а за один раз), то совершенно не важно где стоит (соль или перец).

                                                                                      А вникать в алгоритм (или на худой случай хотя бы на ограничения и/или требования) на самом деле обязательно (и желательно голову включать при этом) — это безопасность все-таки.
                                                                                      Например в этом конкретном случае для прямого соления (первый пример) длинну $pepper_very_long подобрать под SHA2-512 (оптимальная длинна была бы где-то 1024 — 3*8 — 3 = 997 бит или 124.6 байт и желательно не кратна байту, т.е. конкатенацию лучше делать побитно, 3 — здесь минимальная длинна разрешенного пароля).
                                                                                      А для обратного соления/перчения (добиваем пароль ключем — второй пример) нужно брать вместо минимальной длинны разрешенного пароля, максимальную (сильно уменьшаем влияние секретного ключа на коротких паролях) или хотя бы среднюю длинну. Ну или алгоритмически зависимую от длинны пароля, и в этом случае однозначно короче 1024 бита (но не с фиксированной длинной, чтобы усложнить брут).

                                                                                      И как уже говорилось, если нет возможности оценить и вникнуть в алгоритм, а «поперчить» очень хочется просто делаем так.

                                                                                      Выше предлагали использовать для замеса HMAC (вместо сложения) — я бы этого делать не стал, просто потому что я не знаю ни одного человека, который смог бы посчитать вероятности и даже просто риски такого «тройного хэширования» оценить.
                                                                                      Кроме того конкатенация (хоть битовая, хоть байтовая) это дополнительный фактор, улучшающий алгоритм по бруту — ее (на брутах) очень не любят виртексы и иже с ними.
                                                                                        0
                                                                                        > Нет, не совсем правильно: лучше неизвестные взломшику биты в хэшируемой последовательности (именно пароль а не соль) расположить ближе к концу, т.к. они (биты) как раз оказывают наиболее сильное воздействие на результат хэширования.

                                                                                        Не очень понял фразу. Почему биты ближе к концу оказывают наиболее сильное воздействие на результат кеширования? Мне казало, что все биты в «правильном» хеше оказывают равное воздействие на результат
                                                                                          –1
                                                                                          кеширования
                                                                                          Блин, хэширование, а не кэширование. Это разные вещи, как так можно опечатываться?
                                                                                            +1
                                                                                            > Это разные вещи, как так можно опечатываться?

                                                                                            А почему бы и нет?
                                                                                            Я, например, могу опечататься в слове Visualization и получить Virtualization ;)
                                                                                              +1
                                                                                              С утра можно.
                                                                                              0
                                                                                              Только если алгоритм хэширует поблочно-последовательно (не зараз)…
                                                                                              Такая вероятностная характеристика для SHA2 справедлива только при условии, что длинна $pwd + $pepper_very_long больше размера хэшируемого блока (1024 для SHA2-512). Если размер этой последовательности короче размера блока (т.е. хэшируем уже не последовательно, а за один раз), то совершенно не важно где стоит (соль или перец).
                                                                                              0
                                                                                              > Выше предлагали использовать для замеса HMAC (вместо сложения) — я бы этого делать не стал, просто потому что я не знаю ни одного человека, который смог бы посчитать вероятности и даже просто риски такого «тройного хэширования» оценить.
                                                                                              Кроме того конкатенация (хоть битовая, хоть байтовая) это дополнительный фактор, улучшающий алгоритм по бруту — ее (на брутах) очень не любят виртексы и иже с ними.

                                                                                              Вот из-за таких грамотеев, которые собственные спотолочные соображения («это дополнительный фактор») и неграмотность («потому что я не знаю ни одного человека, который смог бы посчитать вероятности») ставят выше официальных рекомендаций организаций вроде IETF — и возникают дырявые парольные системы, описанные в топике.
                                                                                                0
                                                                                                Вот из-за таких грамотеев...
                                                                                                А можно ссылку на рекомендацию IETF, где стоит что результат HMAC_SHA512 можно обернуть bcrypt'ом или хоть каким-нибудь другим алгоритмом. Ну или хотя бы про «перчение» любым MAC-ом?
                                                                                                Кроме того, если речь идет про ту рекомендацию, про которую я думаю, то вот вам цитата:
                                                                                                To achieve this goal, it is important that inputs to KDF are selected from appropriate input distributions and also that inputs are chosen independently of each other (technically, it is necessary that each sample will have sufficient entropy, even when conditioned on other inputs to KDF).
                                                                                                Иначе говоря, вопрос вам на засыпку, грамотей вы наш: Оцените зависимость любой MAC конструкции как псевдослучайной функции на последовательностях UNIQUE_SALT (+) GLOBAL_PEPPER. (Подсказка для UNIQUE_SALT примем энтропию как Hmax, для постоянного GLOBAL_PEPPER энтропия равна нулю).
                                                                                                Рекомендую заглянуть также в родной для HMAC-ов RFC-4868, а именно найти там п. 2.1.3. Randomness and Key Strength.

                                                                                                Кроме того, вспомним для чего были придуманы MAC вообще — для обеспечения целостности сообщений и аутентификации их источника. Ключевое слово «сообщение» (оно в отличии от пароля и соли, как правило длинное).

                                                                                                Ну и справедливое для всех MAC-ов и для HMAC в частности изречение про длинны ключей:
                                                                                                key lengths less than the output length decrease security strength, and keys longer than the output length do not significantly increase security strength
                                                                                                Это к тому, что «откройте для себя HMAC, наконец. Хватит склеивать.»

                                                                                                Грамотеи, блин…

                                                                                                PS. Материал к прочтению для ведения дальнейшей дискуссии. Ну и New Proofs for NMAC and HMAC: Security Without Collision-Resistance желательно хотя бы поверхностно прочитать.
                                                                                                  +1
                                                                                                  Спасибо за обилие полезных ссылок — тема «откройте для себя HMAC» раскрыта полностью:) Извините меня, пожалуйста, за «грамотеев»…
                                                                                            0
                                                                                            Небольшой перчик байт в 13 не помешает + юзайте sha2. И еще ребята из ZF с вами не согласны framework.zend.com/manual/1.12/en/zend.auth.adapter.dbtable.html#zend.auth.adapter.dbtable.advanced.advanced_usage см. Zend_Registry::get('staticSalt')
                                                                                              +8
                                                                                              А я не согласен с ребятами, которые в своем мануале (по Вашей ссылке) рекомендуют использовать MD5.
                                                                                              +3
                                                                                              А кто в курсе, с чем связано ограничение в 72 байта в bcrypt?
                                                                                                +1
                                                                                                Разгадка кроется в функции ExpandKey, а инменно тут:
                                                                                                for(n = 1..18)
                                                                                                    Pn \gets  key[32(n-1)..32n-1] 
                                                                                                
                                                                                                В результате используются 32*18 бита из пароля = 576 / 8 = 72 байта. Перепишите этот цикл подлиннее и будет вам счастье (но это будет уже не bcrypt).

                                                                                                Просто в своей реализации алгоритма Нильс не предпологал входных ключей длиннее 72 байт. Про перчение пароля он и не думал даже, по нескольким причинам:
                                                                                                — алгоритм он разработал еще в 97-м будучи дипломником Гамбурского университета, про «перчение» никаких разговоров тогда не было и в помине;
                                                                                                — в криптографии не отделяют «перчение» от «соления» в принципе — это все одно соль, (другими словами можно же «перчить» соль, как «перчить» и что вообще выступает в виде соли, не является вообще состовляющей алгоритма);
                                                                                                — в качестве соли может выступать все-что угодно, например у одной, достаточно известной фирмы (делал там аудит исходников) в качестве соли выступает хэш-функция выполненая в железе (в виде usb-dongle) по смещению от userid;
                                                                                                — bcrypt.c это алгоритм, если хотите, базовая библиотека, содержащию функцию хэширования, имеющая свои ограничения или требования — правильное ее использование дело рук разработчика модуля, либо реализации интерфейса в конкретном языке.

                                                                                                Требования к алгоритму просты: длинна ключа — 72 байта, длинна соли — 16 байт (в raw виде и 22 байта в base64), все что длиннее обрезаем.

                                                                                                Есть множество имплементаций, где используются надстройки над bcrypt-ом, свободные от этих ограничений.
                                                                                                Например, OpenBSD:bcrypt_pbkdf (pkcs #5 pbkdf2) где пароль и соль предподготавливаются SHA512. Результат — длинна не важна. Про усиление или ослабление алгоритма можно поспорить, но как факт имеем практически неограниченные по длинне ключ (и соль).

                                                                                                Имо, грешить на bcrypt не есть правильно — в некоторых языках просто хромают реализации интерфейса, например в PHP и некоторых реализациях для python можно передать свою соль (например уже перченую), но в результате она хранится вместе с хэшем (the used algorithm, cost and salt are returned as part of the hash), что делает «перчение» ее бессмысленным делом, т.к. она будет хранится в базе уже «перченой». Как раз такой реализацией, они заставляют разраба «перчить» пароль, что в том числе приводит и к таким «ошибкам». То, что требования к алгоритму местами совсем не учтены, тоже не есть гуд.
                                                                                                  +1
                                                                                                  Спасибо, интересная инфа.
                                                                                                +2
                                                                                                Что за детский сад? Почему в минусы перца записывается неумение читать описание функций? Я не говорю, хорошо перец или плохо, я говорю исключительно о методе доказательства, он несерьёзен.
                                                                                                  0
                                                                                                  мда, не понятно как правильно делать…

                                                                                                  Какие уязвимости есть у этой очень простой схемы? (уязвимости, а не недостатки):

                                                                                                  hash = sha256(salt, password)

                                                                                                  соображения:
                                                                                                  1. bcrypt/scrypt — опасность DoS, непонятки с тем что не исследован. не используем.
                                                                                                  2. «перец» не используем.
                                                                                                  3. то что salt стоит до пароля, и чуть-чуть ускорит его поиск — пренебрегаем.
                                                                                                  4. длина соли — 256 бит. просто так. (больше смысла нет)

                                                                                                  p.s. тут конечно не хватает определение уязвимости, давайте считать общепринятую норму (по-моему, не использование scrypt/bcrypt ещё не считается уязвимостью..)

                                                                                                    +1
                                                                                                    Вы ссылаетесь на то, что перец в академических работах не упоминается в рекомендациях. Хотя описанная вами проблема релевантная и для соли.
                                                                                                    Перец можно получать с внешнего источника, например, другой сервер и сохранять его временно в кеше, ну это, чтобы достичь именно перца.
                                                                                                    Доводов почему не использовать соль/перец как-то не вижу.
                                                                                                    То, что вы опубликовали возможную ошибку при использовании соли/перца — спасибо. Но это не значит, что соления и перчения использовать не нужно. Просто не нужно делать таких ошибок.
                                                                                                    Длинна соли в примере неадекватная. Нужно понимать для чего она используется и разницы в использовании 100 символьной и 10 символьной сложной не будет никакой. Да соль будет легче подобрать если ключ зарегистрированного злоумышленника где-то светится в интерфейсе. Дальше можно пробовать составлять радужные таблица для это сайта/приложения. Но ведь предназначение соли как раз в этом и есть – усложнить жизнь злоумышленника, что бы он не смог пользоваться уже готовыми радужными таблицами которых в интернете полно.
                                                                                                      +4
                                                                                                      Печально видеть:

                                                                                                      1) Разработчика, который путает «ошибку реализации», выдавая её за архитектурную, и как следствие проблем ИБ…
                                                                                                      2) Разработчика, который не понимает смысла «перца» в контексте поставленной авторами задачи.
                                                                                                      3) Хабр-сообщество поставившее 150+ плюсов этой «статье».

                                                                                                      В комментах есть ответы на первые два пункта — habrahabr.ru/post/211645/#comment_7284549 8)
                                                                                                        +1
                                                                                                        Ага, давайте теперь и от SSL откажемся, ведь в некоторых его реализациях были найдены RCE. Ой! И сертификаты тоже использовать нельзя, ведь через них SSRF можно делать.
                                                                                                      • UFO just landed and posted this here

                                                                                                        Only users with full accounts can post comments. Log in, please.