Про хранение паролей в БД



    Сегодня посмотрим, как лучше всего хранить пароли в базе данных и как известные платформы решают эту задачу.

    Plaintext


    Когда встал вопрос хранения паролей, конечно, первой идеей было просто записывать их в открытом виде в соответствующей табличке в базе данных. И все бы ничего, если бы доступ к ней действительно напрямую клиенты получить не могли. Но, к сожалению, в различных веб-приложениях по-прежнему иногда работает такая известная всем SQL-инъекция, не говоря уже о других потенциальных уязвимостях. В вопросах безопасности вообще принято предполагать худшее и готовить план действий и защиту даже на такой случай. Будем считать, что злоумышленник нашел в веб-приложении лазейку, тем или иным способом радостно выгружает себе таблицу с именами и паролями пользователей и дальше уже распоряжается ими, как ему вздумается. В общем случае его дальнейшие действия могут быть следующими:

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

    Шифрование Хэширование


    Идея сразу оказывается не такой хорошей. Что делать? Здорово было бы хранить пароли в зашифрованном виде. Тогда, даже если их извлекут, восстановить не смогут или, по крайней мере, потратят на это слишком много времени. Здесь выбор встает между двумя ветками развития: шифровать пароли или хэшировать. Разработчики остановились на втором, и, в принципе, понятно, почему. Сравним наших претендентов по разным характеристикам:

    1. Трудоемкость. Шифрование занимает больше времени, а какое преобразование мы бы ни выбрали, его придется проделывать при каждой проверке пароля. Одним из требований к хэш-функциям же является быстрота выполнения.
    2. Длина выходных значений. Результат шифрования имеет переменную длину, результат хэширования – всегда одинаковую, а хранить однородные по размеру данные в базе данных очень уж удобно. Не говоря уже о том, что длина пароля в зашифрованном виде будет давать некоторую информацию о длине исходного пароля. Одинаковая длина, правда, приводит к возможности возникновения коллизий, но об этом ниже.
    3. Управление ключами. Для шифрования требуется ключ, который тоже где-то придется хранить и надеяться, что его никто не найдет. В любом случае, генерация и управление ключами это отдельная история (они не должны быть слабыми, их нужно регулярно менять и так далее).
    4. Возможность коллизии. При шифровании выходные данные от различных входных даных всегда тоже будут различны. При хэшировании же это не всегда так. Постоянная длина хэша означает ограниченность множества выходных значений хэш-функции, что приводит к возможности коллизии. То есть, допустим, пользователь действительно заморочился и придумал себе по-настоящему классный длинный пароль, в котором есть и спецсимволы, и цифры, и буквы в нижнем и верхнем регистре. Злоумышленник вводит в поле пароля не менее классный пароль “admin”. Сервер для проверки и сравнения хэшей захэшировал его. Хэши совпали. Обидно.

    Таким образом, со счетом 3:1 побеждает хэширование. Но можно ли на этом остановиться?
    Ответ: нет.

    Атаки на хэшированные пароли


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

    • брутфорс по словарю: если с эталонным паролем администраторов у злоумышленника ничего не вышло, он обратится к словарю популярных паролей и попытает счастья с их хэшами;
    • радужные таблицы: вообще сегодня ему, может, не надо будет совсем ничего вычислять и перебирать по словарю. Достаточно будет обратиться к лежащим в сети радужным таблицам. В радужных таблицах содержатся уже вычисленные кем-то до этого хэш-значения и соответствующие им входные данные. Важно отметить, что в силу коллизий, пароль, который предложит радужная таблица, не обязательно будет именно тем, который использует пользователь. Предвычисленные значения есть уже для MD5, SHA1, SHA256, SHA512, а также для их модификаций и некоторых других. Попробовать обратить хэш можно, например, здесь;
    • полный перебор: если не поможет и это, придется прибегнуть к брутфорсу и перебирать подряд все возможные пароли, пока вычисленные хэши наконец не совпадут.

    В самом общем случае злоумышленнику придется брутить пароли. И тут его успех будет зависеть в том числе от быстроты вычисления хэш-функции. Сравнение по времени работы хэшей можно посмотреть здесь. Например, реализованные на Java хэш-функции на 64-битной Windows 10 с 1 core Intel i7 2.60GHz и 16GB RAM были запущены по миллиону раз для вычисления хэша длины в 36 символов. Они показали следующие результаты:

    MD5 – 627 мс
    SHA-1 – 604 мс
    SHA-256 – 739 мс
    SHA-512 – 1056 мс

    А ведь сегодня брутфорс можно распараллелить и выполнить в разы быстрее на GPU (а также на APU, DSP и FPGA). Однако помимо выбора более долгого алгоритма и более длинного выходного результата можно сделать кое-что еще.

    Хэширование хэша


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

    "

    Добавить соль по вкусу


    Для того, чтобы и это он не смог сделать, пароли сегодня хэшируются с добавлением соли.
    Соль – это дополнительная случайная строка, которая приписывается к паролю и хэшируется вместе с ним. Из полученного таким образом хэша по радужной таблице пароль уже не восстановишь. Зная соль и выходной хэш, злоумышленник обречен на брутфорс и никакие заранее вычисленные таблицы ему, скорее всего, не помогут.
    Таксономия соления паролей:

    1. По принципу соления:

    • уникальная соль для каждого пользователя: индивидуальная для каждого пользователя – таким образом, если соль станет известна злоумышленнику, брутить придется пароль каждого по отдельности. И кроме того, даже если два пользователя мыслят одинаково и придумали идентичные пароли, хэши все равно на выходе будут разными;
    • глобальная соль: одинакова для всех, используется для всех хэшей;
    • и то, и другое.

    2. По методу хранения соли:

    • в базе: как правило, индивидуальные соли хранятся в той же базе, что и хэши паролей; часто даже в той же строке;
    • в коде (читать: в конфиге): глобальную соль обычно хранят не в базе данных, а, например, в конфиге, чтобы нарушителю пришлось потратить время на ее подбор.

    Будем считать, что индивидуальные соли пользователей хранятся в базе, глобальная соль в конфиге. Злоумышленник получил доступ к базе, и ему известны все хэши и соответствующие им соли (глобальная соль хранится не в базе, и ее он не знает). Итого, если объединить все способы, то для того, чтобы получить пароли в открытом виде, как было в первых системах, он, будучи крайне целеустремленным, столкнется со следующими препятствиями:

    1. Ему неизвестна глобальная соль, поэтому ее придется брутить.
    2. Ему известны соли пользователей, но заготовленных таблиц с этими солями у него нет, поэтому пароли придется брутить.
    3. Процесс этот займет еще больше времени из-за того, что придется хэшировать хэши по n раз.

    Как хранят пароли различные CMS


    Wordpress


    До версий 3.х пароли просто хэшировались с помощью MD5. Сейчас используется библиотека phpass. По умолчанию к паролю спереди приписывается соль и полученная строка хэшируется MD5 2^8 раз.

    Joomla


    До версии 1.0.12 использовался просто MD5. Используется библиотека phpass, по умолчанию bcrypt с солью и 2^10 повторениями.

    Drupal


    До версии 6 md5 без соли. Используется библиотека phpass. По умолчанию соленый sha512 с 2^16 повторениями.

    Silverstripe


    Использует соленый Blowfish c 2^10 повторениями.

    Umbraco


    Использует HMACSHA256 с солью. Использует вторую, глобальную соль, задаваемую в конфиге.
    Акрибия
    93,00
    Компания
    Поделиться публикацией

    Похожие публикации

    Комментарии 44

      +5

      Без описания PBKDF2, bcrypt (хотя упоминание есть), scrypt, Argon2 пост выглядит неполным


      Ему неизвестна глобальная соль, поэтому ее придется брутить.

      Но ещё есть вариант, когда конфиг с солью тоже сливается через какую-нибудь RCE-уязвимость (или даже тупо по недосмотру с неправильно настроенными правами на файл, пару раз и на такое натыкался)

        +1

        Не сказано про методики на уровне архитектуры — создание небольших микросервисов с задачами "регистрация/логин/удаление", аудит которых прост и дёшев, а выкачивание базы хешей соответственно требует взлома микросервиса, доступного извне, и постоянных запросов к этому микросервису авторизации (привет сетевым экранам и эвристическому анализу трафика между микросервисами). Плюс — hash blinding каким нибудь hmac перед внесением в базу с хранением ключа от блиндинга в HSM.


        meh.

          0
          Еще есть вариант когда уволенный итишник забирает с собой и глобальный конфиг и дб.
          –9
          Когда встал вопрос хранения паролей, конечно, первой идеей было просто записывать их в открытом виде в соответствующей табличке в базе данных.

          Очень плохо, что в 2018 году это все еще первая идея.
          В мире .net есть такая штука как Identity. Там сразу и хранение и изменение и бан. Рекомендую обратить внимание, если гвоздями не прибиты к php.
            +8
            Это не первая идея в 2018 году, это первая идея исторически.
            –3
            Еще в четырнадцатом году глобальную соль раскритиковали в пух и прах. Например Как испортить безопасность паролей, следуя советам с Хабра. Правда там глобальную соль почему-то называют перцем. А ведь злоумышленник вообще может быть сотрудником и иметь доступ и к базе и к исходному коду.

            В то же время есть усовершенствованный вариант хэширования хэша: hash(перец+hash(pass+соль)). Где соль уникальна для каждого пользователя, а перец — для каждой попытки авторизации, и выдается сервером.
              +2

              Гм, а как ваш "усовершенствованный вариант" применим к хранению в БД?

              +3
              Одним из требований к хэш-функциям же является быстрота выполнения.

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

              Если уж говорить про php — то из-за безалаберного отношения к паролям в ядро давно уже включили обёртку password_hash. И использовать надо именно это.
                0
                Строго говоря, все же есть требованиек быстроте: она не должна быть слишком быстрой.
                  +3
                  Это если говорить уже применительно к паролям, а не вообще о хэшировании. А так то хеши применяются много где и много где уместно смещение в сторону производительности. Контроль целостности файлов, например.
                0
                Зачем вообще хранить пароли? Давным давно уже придумали SRP-6a
                  +3

                  Несколько дополнений.


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


                  2. Как уже сказали, пропущен важный момент, почему bcrypt и argon2, а не, например, sha256 с кучей итераций. Как было упомянуто, что есть подбор на GPU и подобном. Вот эти алгоритмы и пытаются "ставить палки в колеса" таким подборам за счет повышения затрат на оперативную память (например, у многоядерных GPU память каждого ядра очень мала, а если каждое ядро начнет обращаться к общей памяти, то скорость подбора стает тех же порядков, что и у CPU).

                  Ну и 3. По поводу придумывания своих алгоритмов, хешей от хешей и т.п. Не делайте так. В криптографии много нюансов, о которых вы не знаете. Например, Timing attack. Используйте проверенные инструменты, предназначенные именно для хеширования и проверки паролей.

                    –1
                    Тут все очень просто, соль — это удлинитель пароля.
                    Красивый бред, Вы сами до этого дошли или кто-то подсказал? Не говорите так больше при людях… Учитывая фиксированную длину выходной последовательности и возможность коллизии с прообразом произвольной длины (не обязательно тоже большой) этот удлинитель можно было бы засунуть очень далеко и глубоко. Соль изначально предназначалась для защиты от простого поиска повторений прообразов (слили базу, узнали/угадали/перебрали 1 простой пароль, а потом выборкой или визуальным осмотром нашли кучу коллег по несчастью для первого бедолаги) с чем она успешно справляется по сей день.
                      +1

                      Видимо вы слишком молоды, что бы знать о радужных таблицах.

                        0
                        Видимо, Вы учили криптографию по Википедии.
                          +1

                          Говорить о криптографии с человеком, говорящим о подборе пароля через коллизии? Смешно.

                            0
                            Не смешнее, чем говорить о криптографии с домохозяйкой и любителем «удлинителей». И замечу, что ни слова про подбор паролей в предложении о коллизиях не было… Хотя вы уже сами дали себе верную оценку:
                            В криптографии много нюансов, о которых вы не знаете.
                              +1

                              Ну вы же сами сказали, что длинная соль плохо, ибо "возможность коллизии". Но, видимо, вы совсем не это имели ввиду, а что имели, то не в виду ;) Видимо вас волнует вероятность коллизии… или нет? Уж объяснитесь.


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

                                0
                                Ну вы же сами сказали, что длинная соль плохо, ибо «возможность коллизии».
                                Интересный плод Вашего воображения. Такого в мою голову даже придти не могло… Я говорил, что ОЦЕНКА соли, как удлинителя — мимо темы. И я ни как не оценивал плоха длинная соль или хороша.
                                Конкретно имелось ввиду, что добавление соли несёт не тупо «удлинительный» характер, а увеличивает энтропию. И поэтому говорить «с солью длиннее! А значит лучше» — бред, учитывая, что хеш-функция в итоге свернёт, что 5, что 25 символов — ей без разницы. И не гарантируется, что именно вот к этим 25 нет коллизии, скажем, с прообразом гораздо меньшей длины. И поэтому оценка длины прообраза (с солью или без) практически ни какого смысла не имеет (ну за исключением защиты от тупого перебора (полного или по словарю).
                                Конечно длина увеличится — это факт. Но это не ставилось целью в идеи подсаливания хешей. Повторяю — в первую очередь это защита от простого поиска повторений прообразов. Всё остальное — следствия.
                                  +1

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


                                  "Маскирование" одинаковых паролей — вторая задача.


                                  А коллизии тут вообще отношения не имеют. Не ищут коллизии.

                    0

                    hmac384 или sha384(пароль+глобальное чтонить). Про потери перфоманса… только на тестах в вакууме.

                      0

                      Разве многократное хеширование не увеличивает вероятность коллизий?

                        +1
                        Многократное хэширование увеличивает вероятность коллизий, но это не критично и многие его используют.
                        Об этом можно прочитать здесь: habr.com/post/100301
                          0

                          Всегда интересовала эта тема. Спасибо за ссылку.

                        +2
                        Реально хэшируют 2^8 раз подряд? О_о
                        Это действительно помогает?

                        Я для своего сервиса сделал просто хэш(индивидуальная_соль+хэш(хэш(имя_пользователя)+хэш(пароль_пользователя)))
                        Неужели этого недостаточно?
                          –2

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


                          Для сравнения: в Django по умолчанию используется PBKDF2, в котором SHA256-хэш хэширует 150000 (прописью: сто пятьдесят тысяч) раз подряд

                            0
                            Простите, но зачем?..
                              –1

                              Чтобы на GPU не перебрали, очевидно же :)


                              Я тут по случайному совпадению запустил перебор одного md5-хэша за пару дней до этого поста — на дохлом ноутбучном GPU. 8 и 9 символов уже перебраны, 10 символов переберутся через полтора месяца. Если обзавестись хотя бы двумя хорошими нвидиями, можно будет и 10 символов за пару дней перебрать) И это если не вспоминать пришедшие из мира криптовалют ASIC и FPGA, которые заточены именно для такого перебора (только в контексте всяких там блокчейнов, но по сути то же самое).


                              А соль и имя пользователя никак не затруднят перебор — они же заранее известны, и злоумышленник исследователь просто сразу их подставит в алгоритме, перебирая только собственно пароль

                                0
                                А соль и имя пользователя никак не затруднят перебор — они же заранее известны, и злоумышленник исследователь просто сразу их подставит в алгоритме, перебирая только собственно пароль


                                Только если знает алгоритм конкатенации промежуточных строк, да?
                                  –1

                                  Безопасность через неясность это плохо, стоит автоматически предполагать, что вместе с базой слит и код сайта, в котором есть этот алгоритм (к тому же мне самому доводилось сливать код сайтов пару раз, хех)

                                    0
                                    А если не сайт, а сервер игры? Хостящийся на амазоне.

                                    Извиняюсь, я просто в теме профан, но в качестве хобби пилю клиент-серверную игру, и вот как раз недавно делал систему авторизации)
                                      –1

                                      А в чём принципиальная разница-то? С точки зрения сливов сервер игры тоже не обязательно безопасен и теоретически может обеспечить слив через какую-нибудь дырку. Разница разве что в нагрузке алгоритма на сервер, но у PBKDF2 даже с 150000 итераций нагрузка всё равно небольшая — это не какой-нибудь Argon2, который придуман специально чтобы выжирать и проц, и оперативку)) (а придуман он тоже именно для того, чтобы затруднить перебор на GPU)


                                      Ну или можно включить режим неуловимого Джо и заявить что-то вроде «да кому нахрен нужен этот мой сервер игры, никто ничего подбирать не будет». Именно по этому принципу на двух моих старых сайтах до сих пор используются несолёные md5 и sha1 :D

                                        0
                                        Спасибо большое, изучу тему поглубже)
                                    –1

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

                                    0
                                    никак не затруднят перебор

                                    Разница очевидна если конечная цель подобрать пароли пользователей. Сравните:
                                    ХЕШ("12345") -> нашли 29 пользователей с таким паролем.
                                    ХЕШ("12345" + ИНДИВ.СОЛЬ) -> нашли 1 пользователя с таким паролем. Но ещё нужно обнаружить 28 пользователей с таким же паролем, но другим итоговым хешем.
                                      –1
                                      Всё так, но если в качестве хэша используется простой md5 или типа того, то точно так же перебрать остальных пользователей займёт миллисекунды, даже если их миллионы) А ещё это всё будет не иметь значения, если целью является подбор пароля одного конкретного пользователя или поиск наибольшего числа пользователей с паролями вида 123456 (перебрать всех по словарю не так уж долго даже с солью)
                                        +1
                                        Вы опять не уловили разницу. У вас есть словарь из 1000 слов и миллион пользователей. В одном случае для полного перебора надо сгенерировать 1000 хешей, в другом 1.000.000.000 (миллиард Карл!).
                                          –1
                                          Моя ноутбучная видеокарта считает 2.000.000.000 md5-хэшей в секунду. Два миллиарда в секунду, Карл! Ноутбучная, Карл!
                                    0

                                    Чтобы вычислительную сложность повысить.

                                0

                                Интересно, если хранить в БД не соленые многораундные хэши, а зашифрованную пару соль+хэш пароля. Можно также делать многораундное шифрование. Можно даже шифровать ассиметрично и хранить приватный ключ рядом с БД.
                                Плюсы: 1) асиметричный шифр более ресурсоемкий, чем любой хэш, и плохо брутится. 2) По определению нет радужных таблиц. 3) Даже, если сольют базу и приватный ключ, в лучшем случае получат хэш пароля и соль

                                  –1
                                  Что-то я не понял, если при сливе всё равно «получат хэш пароля и соль», то какой смысл в шифровании?
                                    0
                                    Во-1х, слить нужно и БД и приватный ключ, что гораздо-гораздо сложнее. Например, ключ может располагаться на физическом токене с крипточипом. Во-2х, даже если это каким-то образом удастся, остается последний эшелон защиты — многораундное шифрование соленого хэша с его же солью. Ассиметричный шифр на GPU уже не погоняешь, таблицы не подходят, остается только жесткий брут на CPU.
                                      –1

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

                                        +1
                                        Не спорю, но тут вроде статья и обсуждение про способы хранения паролей в БД. А так-то, конечно, можно придумать что угодно другое.

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

                                Самое читаемое