Best way to store password in database [closed]
It also connects to a database, so I figured I would store the username and password there. However, it seems like not such a good idea to have passwords as just a text field in a table sitting on the database.
I’m using C# and connecting to a 2008 express server. Can anyone suggest (with as many examples as possible) what the best way to store this type of data would be?
P.S I am open to the idea that this info not be stored in the database if a good reason can be provided
8 Answers 8
You are correct that storing the password in a plain-text field is a horrible idea. However, as far as location goes, for most of the cases you’re going to encounter (and I honestly can’t think of any counter-examples) storing the representation of a password in the database is the proper thing to do. By representation I mean that you want to hash the password using a salt (which should be different for every user) and a secure 1-way algorithm and store that, throwing away the original password. Then, when you want to verify a password, you hash the value (using the same hashing algorithm and salt) and compare it to the hashed value in the database.
So, while it is a good thing you are thinking about this and it is a good question, this is actually a duplicate of these questions (at least):
To clarify a bit further on the salting bit, the danger with simply hashing a password and storing that is that if a trespasser gets a hold of your database, they can still use what are known as rainbow tables to be able to «decrypt» the password (at least those that show up in the rainbow table). To get around this, developers add a salt to passwords which, when properly done, makes rainbow attacks simply infeasible to do. Do note that a common misconception is to simply add the same unique and long string to all passwords; while this is not horrible, it is best to add unique salts to every password. Read this for more.
zmts / passwords.md
Суть не в дешифровке хеша из базы и сверке его с введенным паролем, а в создании нового хеша из нового введенного юзером пароля и соли из базы и последующей сверкой нового хеша с хешем из БД.
Практическое применение: создание хеша
Практическое применение: проверка хешей
- Берем пароль из инпута, хеш из БД(hash_from_db)
- Ф-ция compare сама извлекает соль из хеша(hash_from_db) и солит им нововведенный пароль
- И сверивает новый хеш с хешем из БД(hash_from_db) Вот почему мы не сохраняли соль в БД, bcryptjs сам знает как извлечь соль из хеша, и унас отпадает необходимость хранить соль отдельно
Откуда bcrypt.compare берет соль
Первые 29 символов и есть соль $2a$10$ydKFuX0Jfxsega193bReze
Про хранение паролей в БД
Когда встал вопрос хранения паролей, конечно, первой идеей было просто записывать их в открытом виде в соответствующей табличке в базе данных. И все бы ничего, если бы доступ к ней действительно напрямую клиенты получить не могли. Но, к сожалению, в различных веб-приложениях по-прежнему иногда работает такая известная всем SQL-инъекция, не говоря уже о других потенциальных уязвимостях. В вопросах безопасности вообще принято предполагать худшее и готовить план действий и защиту даже на такой случай. Будем считать, что злоумышленник нашел в веб-приложении лазейку, тем или иным способом радостно выгружает себе таблицу с именами и паролями пользователей и дальше уже распоряжается ими, как ему вздумается. В общем случае его дальнейшие действия могут быть следующими:
- выполнение нелегитимных действий от имени пользователей с использованием их учетных данных на уязвимом ресурсе: например, к учетной записи привязана банковская карта, и теперь злоумышленник может ей пользоваться;
- попытка использования полученного пароля на других ресурсах: далеко не всегда пользователи, следуя советам, придумывают каждый раз новые пароли для разных сервисов;
- попытка выявить правило генерации пароля и перейти ко второму пункту: некоторые формируют какое-то правило составления пароля, в итоге на разных ресурсах пароли разные, но подчиняются одному и тому же правилу, которое можно выявить;
- повышение привилегий: в той же таблице может храниться и пароль администратора, со знанием которого иногда можно получить полный контроль над сервером.
Шифрование Хэширование
Идея сразу оказывается не такой хорошей. Что делать? Здорово было бы хранить пароли в зашифрованном виде. Тогда, даже если их извлекут, восстановить не смогут или, по крайней мере, потратят на это слишком много времени. Здесь выбор встает между двумя ветками развития: шифровать пароли или хэшировать. Разработчики остановились на втором, и, в принципе, понятно, почему. Сравним наших претендентов по разным характеристикам:
- Трудоемкость. Шифрование занимает больше времени, а какое преобразование мы бы ни выбрали, его придется проделывать при каждой проверке пароля. Одним из требований к хэш-функциям же является быстрота выполнения.
- Длина выходных значений. Результат шифрования имеет переменную длину, результат хэширования – всегда одинаковую, а хранить однородные по размеру данные в базе данных очень уж удобно. Не говоря уже о том, что длина пароля в зашифрованном виде будет давать некоторую информацию о длине исходного пароля. Одинаковая длина, правда, приводит к возможности возникновения коллизий, но об этом ниже.
- Управление ключами. Для шифрования требуется ключ, который тоже где-то придется хранить и надеяться, что его никто не найдет. В любом случае, генерация и управление ключами это отдельная история (они не должны быть слабыми, их нужно регулярно менять и так далее).
- Возможность коллизии. При шифровании выходные данные от различных входных даных всегда тоже будут различны. При хэшировании же это не всегда так. Постоянная длина хэша означает ограниченность множества выходных значений хэш-функции, что приводит к возможности коллизии. То есть, допустим, пользователь действительно заморочился и придумал себе по-настоящему классный длинный пароль, в котором есть и спецсимволы, и цифры, и буквы в нижнем и верхнем регистре. Злоумышленник вводит в поле пароля не менее классный пароль “admin”. Сервер для проверки и сравнения хэшей захэшировал его. Хэши совпали. Обидно.
Атаки на хэшированные пароли
Итак, злоумышленник заполучил нашу таблицу с именами пользователей и паролей. Пароли теперь захэшированы, но это нашего атакующего не останавливает, и он всерьез намерен их восстановить. Его возможные действия:
- брутфорс по словарю: если с эталонным паролем администраторов у злоумышленника ничего не вышло, он обратится к словарю популярных паролей и попытает счастья с их хэшами;
- радужные таблицы: вообще сегодня ему, может, не надо будет совсем ничего вычислять и перебирать по словарю. Достаточно будет обратиться к лежащим в сети радужным таблицам. В радужных таблицах содержатся уже вычисленные кем-то до этого хэш-значения и соответствующие им входные данные. Важно отметить, что в силу коллизий, пароль, который предложит радужная таблица, не обязательно будет именно тем, который использует пользователь. Предвычисленные значения есть уже для MD5, SHA1, SHA256, SHA512, а также для их модификаций и некоторых других. Попробовать обратить хэш можно, например, здесь;
- полный перебор: если не поможет и это, придется прибегнуть к брутфорсу и перебирать подряд все возможные пароли, пока вычисленные хэши наконец не совпадут.
MD5 – 627 мс
SHA-1 – 604 мс
SHA-256 – 739 мс
SHA-512 – 1056 мс
А ведь сегодня брутфорс можно распараллелить и выполнить в разы быстрее на GPU (а также на APU, DSP и FPGA). Однако помимо выбора более долгого алгоритма и более длинного выходного результата можно сделать кое-что еще.
Хэширование хэша
Чтобы помешать нарушителю воспользоваться готовыми радужными таблицами, существует техника хэширования пароля несколько раз. То есть вычисляем хэш от хэша от хэша от хэша… и так n раз (надо, правда, сильно с этим не увлекаться, потому что при обычной проверке пароля пользователя серверу тоже придется это проделывать). Теперь так просто по радужной таблице он пароль не найдет, да и время на брутфорс заметно увеличится. Но ничто не остановит злоумышленника от того, чтобы сгенерировать радужную таблицу по словарю паролей, зная алгоритм хэширования. Тем более, для самых популярных комбинаций этого метода такие таблицы уже сгенерированы:
«
Добавить соль по вкусу
Для того, чтобы и это он не смог сделать, пароли сегодня хэшируются с добавлением соли.
Соль – это дополнительная случайная строка, которая приписывается к паролю и хэшируется вместе с ним. Из полученного таким образом хэша по радужной таблице пароль уже не восстановишь. Зная соль и выходной хэш, злоумышленник обречен на брутфорс и никакие заранее вычисленные таблицы ему, скорее всего, не помогут.
Таксономия соления паролей:
1. По принципу соления:
- уникальная соль для каждого пользователя: индивидуальная для каждого пользователя – таким образом, если соль станет известна злоумышленнику, брутить придется пароль каждого по отдельности. И кроме того, даже если два пользователя мыслят одинаково и придумали идентичные пароли, хэши все равно на выходе будут разными;
- глобальная соль: одинакова для всех, используется для всех хэшей;
- и то, и другое.
- в базе: как правило, индивидуальные соли хранятся в той же базе, что и хэши паролей; часто даже в той же строке;
- в коде (читать: в конфиге): глобальную соль обычно хранят не в базе данных, а, например, в конфиге, чтобы нарушителю пришлось потратить время на ее подбор.
- Ему неизвестна глобальная соль, поэтому ее придется брутить.
- Ему известны соли пользователей, но заготовленных таблиц с этими солями у него нет, поэтому пароли придется брутить.
- Процесс этот займет еще больше времени из-за того, что придется хэшировать хэши по 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 повторениями.
Как хранить пароли в базе данных
Хеширование — преобразование массива входных данных произвольной длины в (выходную) битовую строку фиксированной длины, выполняемое определённым алгоритмом. Функция, реализующая алгоритм и выполняющая преобразование, называется «хеш-функцией»(реже «функцией свёртки»). Исходные данные называются входным массивом, «ключом» или «сообщением». Результат преобразования (выходные данные) называется «хешем», «хеш-кодом», «хеш-суммой», «сводкой сообщения».
CRC32 (Cyclic redundancy check — Циклический избыточный код) простая хэш функция разработанная для защиты данных от случайных изменений в компьютерных устройствах, таких как сетевые карты и жёсткие диски. CRC32 определяется международным стандартом CRC32-IEEE 802.3 Алгоритм очень быстр и, несмотря на полную криптографическую незащищённость, широко используется благодаря простоте реализации и скорости. 32-битный хэш-код обычно представляется шестнадцатеричным числом из 8 символов. MD4 алгоритм хэширования, разработанный Рональдом Л. Ривестом из RSA Data Security, Inc. В настоящее время считается ненадёжным. Это быстрый алгоритм (на 32-битных процессорах) и его используют при вычислении хэшей в peer-to-peer сети EDonkey 2000. Алгоритм описан в RFC 1320. Хэш-код представляет шестнадцатеричное число из 32 символов. MD5 ещё один алгоритм хэширования, разработанный Рональдом Л. Ривестом из RSA Data Security, Inc. Представляет улучшенную версию MD4. Алгоритм описан в RFC 1321. В течении многих лет MD5 был стандартом интернет, но сейчас считается сломанным. Хэш-код представляет шестнадцатеричное число из 32 символов. SHA1 (Secure Hash Algorithm 1) алгоритм хэширования, разработанный NSA в 1993. Описан в RFC 3174. Он примерно в 2-3 раза медленнее алгоритма MD5. Хэш-код представляет шестнадцатеричное число длины 40. Tiger современная хэш-функция изобретённая Россом Андерсом и Эли Бихамом. Была специально придумана такой, чтобы быстро вычисляться на 64-битных процессорах. См. описание. Хэш-код представляет шестнадцатеричное число длины 48. TTH (Tiger Tree Hash) хэш, вычисляющийся в древовидной форме с использованием алгоритма Tiger. См. описание. TTH используется в нескольких peer-to-peer сетях: Direct Connect, Gnutella, Gnutella2, а также в таких программах как DC++, Phex и Shareaza. Хэш-код представляет base32-закодированную строку длины 39. BTIH (BitTorrent InfoHash) используется в p2p сети BitTorrent. Хэш-сумма зависит не только от данных, но и от имени файла и даже от программы вычисляющей хэш. RHash использует тот же метод, что и uTorrent. Хэш является строкой из 40 шестнадцатеричных чисел. EDonkey 2000 один из самых быстрых алгоритмов хэширования. Он основан на устаревшем MD4 и используется в p2p сети EDonkey. Описание алгоритма: EDonkey network. Хэш-код представляет шестнадцатеричное число из 32 символов. AICH (Advanced Intelligent Corruption Handler) хэш, вычисляющийся в древовидной форме с использованием алгоритма SHA1. См. описание. Хэш используется в p2p сети EDonkey и обычно включается в EDonkey-линки. Хэш-код представляет base32-закодированную строку длиной в 32 символа. WHIRLPOOL хэш функция, рекоммендованная проектом NESSIE и принятая как ISO/IEC 10118-3:2004 международный стандарт. Хэш представляется 128-цифровым шестнадцатеричным числом. GOST хэш-функция определённая в российском государственном стандарте ГОСТ Р 34.11-94. Существует две широко распространённые версии алгоритма — с «тестовым» и CryptoPro наборами параметров. Хэш функция медленнее аналогичных, но используется для цифровой подписи в российских государственных банках и предприятиях. Хэш представляет шестнадцатеричную строку длины 64. HAS-160 корейская хэш-сумма, разработанная для корейского алгоритма цифровой подписи (KCDSA). Хэш представляет шестнадцатеричную строку длины 40. RIPEMD-160 (RACE Integrity Primitives Evaluation Message Digest) — 160-битная хэш-функция. Спецификация: веб-страница RIPEMD-160. Хэш представляет шестнадцатеричную строку длины 40. EDON-R семейство хэш-функций с произвольной длиной хэша. RHash поддерживает 256-битный и 512-битный варианты. 512-битный EDON-R является одним из самых быстрых алгоритмов на 64-битных процессорах. Хэш функция описана в статье: D. Gligoroski, S. Markovski, L. Kocarev Edon-R, An Infinite Family of Cryptographic Hash Functions (2006). 256-битный и 512-битный хэши представляют строку длиной 64 и 128 шестнадцатеричных символов соответсвенно. Snefru хэш функция разработанная Ральфом Меркле в 1990 и названная в честь египетского фараона Sneferu. В настоящее время она считается сломанной, к тому же она значительно медленнее современных хэш функций. Хэш 128-битного и 256-битного Snefru представляется шестнадцатеричной строкой длины 32 и 64 символа соответственно.128-битного и 256-битного Snefru представляется шестнадцатеричной строкой длины 32 и 64 символа соответственно.
Хеширование применяется в следующих случаях:
- при построении ассоциативных массивов
- при поиске дубликатов в сериях наборов данных
- при построении уникальных идентификаторов для наборов данных
- при вычислении контрольных сумм от данных (сигнала) для последующего обнаружения в них ошибок (возникших случайно или внесённых намеренно), возникающих при хранении и/или передаче данных
- при сохранении паролей в системах защиты в виде хеш-кода
- при выработке электронной подписи
Хранение логин/пароля в БД
Рассмотрим пример хранения пары логин/пароль. Первый вариант.
id | usr | passwd |
---|
В качестве примера во втором варианте первичным ключом является логин, т.е. столбец usr .
usr | passwd |
---|
Хранение пароля в открытом виде
Храним пароль как есть. Например, если ввели пароль «12345», то он так и сохранится. Минус в том, что при взломе БД злоумышленник получает все пароли в явном виде.
usr | passwd |
---|---|
user | password |
prof | 123456 |
pety | qwerty |
Храним пароль в зашифрованном
Самый простой способ хранить пароли в базе это положить их в таблице пользователей в открытом виде. Но при таком подходе, в случае попадания базы в руки злдоумышленника, все пароли 100% становятся ему известны.
Чтобы этого не произошло, пароль лучше хранить в зашифрованном виде, использовав, к пример, алгоритмы шифрования md5 , sha1 и т.п.
usr | passwd |
---|---|
user | 5f4dcc3b5aa765d61d8327deb882cf99 |
prof | e10adc3949ba59abbe56e057f20f883e |
pety | d8578edf8458ce06fbc5bb76a58c5ca4 |
Использование коллизии
Коллизия хеш-функции возникает, когда она выдает одинаковый результат на разные входные данные. Конечно же, вероятность этого достаточно мала, и зависит от длины хэша. Однако устаревшая (но до сих пор иногда используемая) функция crc32 возвращает в качестве хэша 32-битное целое число. Т.е., чтобы подобрать пароль к такому хэшу, по теории вероятности нужно получить 2^32 = 4 294 967 296 различных хэшей. Даже на хостинге crc32 может работать примерно со скоростью 350 000 хешей в секунду(посчитайте сами сколько нужно секунд, чтобы взломать такой хэш).
Представим что «плохой дядька» украл базу данных вместе с хэшированным паролем. У него не будет возможности преобразовать 323322056 обратно в «supersecretpassword», однако, благодаря простому скрипту он можем подобрать другой пароль, который в хэшированном виде будет точно такой же как и тот, который находится в базе.
К примеру для нашего пароля «supersecretpassword» найдётся строка «MTIxMjY5MTAwNg= func»>echo crc32( «supersecretpassword» ); // 323322056 echo crc32( «MTIxMjY5MTAwNg= phpcomment»>// 323322056
Даже на домашнем компьютере можно использовать миллионы хэш функций в секунду. Поэтому необходимо использовать такую хэш функцию, которая сгенерировала как можно большее значение.
Рекомендую использовать алгоритмы линейки SHA (на данный момент практически заверщён переход от SHA-1 к стандартам версии SHA-2). SHA-2 — это семейство криптографических функций SHA224, SHA256, SHA384 и SHA512. SHA224 и SHA384 являются по сути аналогами SHA256 и SHA512 соответственно, только после расчета свертки часть информации в ней отбрасывается.
- MD5: 16000 M/s
- SHA-1: 5900 M/s
- SHA256: 2050 M/s
- SHA512: 220 M/s
Радужная таблица
Радужные таблицы состоят из хэшей наиболее часто употребляемых паролей — имен, дат рождения, названий животных и т.п. Эти таблицы могут включать миллионы, миллиарды значений, но работа с ними относительно быстра, и проверить хэш на соответствие одному из значений не составляет никакого труда.
Рассмотрим ситуацию. У Вас есть хешированный пароль пользователя, и хранится он в таблице базы данных. Даже если злоумышленник получает доступ к нашей базе данных он не сможет определить исходный пароль. Но что если он сравнивает все хэшированные пароли друг с другом, и находит некоторые из них как быть?
user | 5f4dcc3b5aa765d61d8327deb882cf99 |
. | . |
pety | 5f4dcc3b5aa765d61d8327deb882cf99 |
Мы уже знаем две строки могут иметь одинаковый хеш, только если они обе равны(без учёта коллизии). Значит, если атакующий видит хеши он может сделать вывод о том что пароли для этих учетных записей одинаковые. Если атакующий, знает хотя бы один пароль к аккаунту, он может его использовать для получения доступа ко всем аккаунтам с этим паролем.
«Солим» пароль
Одним из решением проблемы радужных таблиц является использование случайного числа при генерации хеша, так называемая соль.
Так для нашего примера применим соль.
usr | passwd |
---|---|
user | 8da1a876eabe199515de1766b87a602a5d06d378 |
prof | 3c9ced61cf158c57bff7cad5d72ae9ceb17e565a |
pety | 58131ab13b4af6894bee5ea6b6999cd90c159239 |
Статическая соль и тому подобные конструкции могут служить достаточно хорошо, пока структура этих конструкций и соль хранятся в тайне. Если же злоумышленник вызнает секрет хэширования — он с легкостью сможет модифицировать под него свою радужную таблицу.
Уникальная соль
Каждый раз, когда мы генерируем хэш пароля, необходимо использовать случайную соль. Просто нужно генерировать случайные числа(строки) определенной длины и добавить его в простой текстовый пароль, а затем его хеш. Таким образом, даже если пароли для двух аккаунтов одинаковые с генерированный хеш не будет одинаковым, потому что числа соли в одинаковых случаях различные.
Конечно, уникальную соль придется вносить в базу данных, но даже получив доступ к ней, злоумышленник вряд ли сможет сгенерировать несколько миллионов радужных таблиц.
Принудительное замедление
Как говорилось ранее, компьютер с мощной графической картой может высчитывать миллион хэшей за секунду. Злоумышленники могут применить «грубую силу»(bruteforce), проверяя каждый единственно возможный пароль (проводя полный перебор всех возможных вариантов).
Если в пароле используются символы в нижнем и верхнем регистрах и цифры, то общее количество возможных символов составит 62 (26+26+10).
Для пароля длиной в 8 символов, существует 62^8 различных комбинаций (порядка 218 триллионов). Со скоростью в 1 миллиард хэшей в секунду (достаточно маленькая для брутфорс-атаки), пароль будет сломан примерно за 60 часов. А для наиболее распространенной длины пароля в 6 символов, длительность расшифровки составит меньше двух минут.
Лучше использовать более медленные функции хэширования или например можно замедлить функцию хэша вручную: