Правильно храним пароли в БД
Как известно, хранить пароль в открытом виде в базе данных или еще где-то совсем не безопасно. Нелепо также наивно полагать, что если к базе имею доступ только я, то никто не сможет проникнуть внутрь, кроме меня, потому что только я знаю пароль.
К сожалению, ошибки в скриптах и различные уязвимости иногда позволяют прочесть данные с помощью сформированных специальным образом SQL-запросов. Разрабатывая какой-то скрипт, а может быть и большой проект, нужно заранее полагать, что доступ к таблице, хранящей какие-то пароли, все равно может быть получен.
Поэтому, вывод напрашивается сам собой – нужно хранить не пароли в открытом виде, а их хэш-значения. Более того, нужно хранить не чистые хэш-значения, а как бы модифицированные
Минус тут единственный – при запросе восстановления пароля пользователем мы не сможем дать ему этот пароль, т.к. не знаем его, но можем сгенерировать новый и отправить ему по почте (или на ушко шепнуть, если он рядом
).
Сегодня мы попробуем сотворить метод, который будет превращать наш пароль не просто в хэш, из которого итак сложно получить исходный пароль (это справедливо не всегда), а хэш, полученный из пароля и так называемой соли – добавке к паролю, которая затем хэшируется и получается результат в виде нового хэша.
Сначала о том, как мы можем получить пароль из хэша. Прямым преобразованием конечно никак, т.к. хэш – необратим. Мы можем воспользоваться например брутфорс-атакой (т.е. перебором всех возможных комбинаций), или атакой по словарю (а вдруг пользователь задал в качестве пароля несложную последовательность символов, например, какое-нибудь слово или сочетания цифр).
Я предлагаю пароль хэшировать алгоритмом SHA1, затем прибавлять к нему соль и еще раз хэшировать полученный результат. Затем все это (полученный хэш и соль) сохранять в базе данных.
Соль – это некоторая последовательность байт, которая прибавляется каким-то образом к паролю (в нашем случае – к его хэшу). Подробнее про соль в криптографии можно прочитать в википедии:
Сейчас некоторые из тех, кто читают эту статью, наверняка начали критиковать меня. Из-за чего? Потому что храним хэш и соль в одном месте – в базе. Т.е. если злоумышленник знает алгоритм формирования пароля и достанет хэш (хотел написать хлеб
) с солью, то он без труда сможет организовать атаку по словарю или брутфорс. Да, тут сложно не согласиться, но согласитесь и Вы со мной – сложность перебора в нашем случае увеличивается и такая брутфорс атака будет производиться медленнее, чем если бы это был просто поиск совпадений хэш-значений (с хеш-значением пароля), полученных в результате перебора паролей. Но зачастую код, отвечающий за формирование пароля, скрыт от злоумышленника. Потом, если пользователя заставлять запоминать соль вместе с паролем – ему будет сложнее. Лично я не видел еще сайтов, которые спрашивают у тебя не только пароль, но еще и соль (куда ее ему дать? На монитор насыпать может?
. Ааа, ну да. В textbox ввести в дополнительный
).
Да и пароли нужно выбирать посложнее.
Привожу пример функции, генерящей сложный пароль заданной длины (это учебный пример, можно конечно придумать что-то получше):
function createpwd($length) {
$chars = Array(
'a','b','c','d','e','f',
'(',')','[',']','!','?',
'g','h','i','j','k','l',
'&','^','%','@','*','$',
'm','n','o','p','r','s',
'<','>','/','|','+','-',
't','u','v','x','y','z',
'A','B','C','D','E','F',
'G','H','I','J','K','L',
'M','N','O','P','R','S',
'T','U','V','X','Y','Z',
'1','2','3','4','5','6',
'7','8','9','0','.',',',
'{','}','`','~'
);
$pwd = '';
$chars_count = count($chars);
for ($i = 0; $i < $length; $i++) {
$index = rand(0, $chars_count - 1);
$pwd .= $chars[$index];
}
return $pwd;
}
Генерируем пароль:
$pwd = createpwd(10);
Теперь нам нужно сгенерировать соль. Не будем писать для этого свою функцию – воспользуемся уже представленной. Пусть соль будет длиной в 6 случайных символов:
$salt = createpwd(6);
Теперь создаем хэш-значение из пароля и соли:
$hash = sha1($salt.sha1($pwd).$salt);
После всего этого мы должны сохранить содержимое переменных $salt и $hash в базу (не буду объяснять, как – думаю, все знают), а содержимое переменной $pwd как-то показать пользователю (а можно и на ушко шепнуть, как я уже писал).
При авторизации пользователя спрашиваем у него пароль и сохраняем его в переменную $pwd, затем берем из базы $salt и $hash и проверяем, правильно ли ввел пароль пользователь:
if (sha1($salt.sha1($pwd).$salt) == $hash) {
echo ‘можешь проходить’;
} else {
echo ‘попробуй еще раз, но не хулигань!’;
}
При таком раскладе лично я думаю, что взломать пароль будет очень сложно, особенно если он состоит не только из букв и цифр, но еще и из различных символов.
А на последок небольшой сюрприз. Помните, несколькими строчками выше я писал, что использую sha1? А ведь можно было md5. Почему я не использовал md5: оказывается, в интернете есть такой сервис, который хранит в своей базе огромное количество вычисленных хэшей. Т.е. достав какой-то md5-хэш, можно посмотреть, какие пароли по нему могут быть получены. Адрес этого сервиса – . Может быть есть еще какие-нибудь подобные сайты, но я не искал.
На сегодня все.
Всего доброго!
Февраль 13th, 2009
Позволю себе добавить, что использование случайной соли хорошо ещё вот в каком плане: если злоумышленник зарегистрирует на сайте несколько аккаунтов с распространёнными паролями, а потом украдёт базу, он не сможет по совпадению хэшей найти логины пользователей с этими простыми паролями.
А почему вы считаете, что если злоумышленник знает алгоритм шифрования, соль и хэш, это усложнит брутфорс? Ведь он как перебирал только варианты пароля (в случае отсутствия соли), так и будет перебирать.
Кстати, насколько я знаю, Rainbow-таблицы (на которых основана работа того же GData) есть не только для md5, но и для sha1, так что он не намного безопаснее.
Февраль 13th, 2009
Спасибо за столь подробную информацию, пароль это неотъемлемая часть…..и главное что он был надежным!!
Февраль 13th, 2009
Процесс перебора должен усложниться в плане времени. Для перебора должно потребоваться больше времени, чем если бы это было хэширование без соли. Насчет Rainbow-таблиц я так и предполагал, что есть и для других алгоритмов. Так что тут надо скорее всего сочетать алгоритмы хэширования, чтобы нельзя было воспользоваться такими таблицами.
Февраль 13th, 2009
А почему потребуется больше времени?
Допустим, злоумышленник знает, что
$hash = md5($salt.$password);
и вытащил из базы
$hash = ‘6547436690a26a399603a7096e876a2d’;
$salt = ‘bbb’;
Он будет перебирать варианты $password и смотреть, какой даст правильный хэш при таком алгоритме. В случае, если бы “соль” не использовалась, он перебирал бы такие же пароли и нашёл бы правильный за такое же число попыток. Или вы что-то другое имели в виду?
Февраль 13th, 2009
Я имел в виду, что sha1($salt.sha1($pwd).$salt) должно длиться дольше, чем просто sha1($salt.$pwd)
Февраль 13th, 2009
Я обычно шифрую пароли сначала md5 потом sha1 + добавляю соль
Февраль 25th, 2009
Вообще говоря сложность алгоритмов шифрования данных во многом определяется задачей. Например если мне нужно хранить пароль от профиля на форуме зачем его “солить”. Хранить md5($pwd) да и хватит.
Март 2nd, 2009
Написано всё верно. Не подкопаешься.
Единственное что мне не понятно – это по поводу сервиса с md5 хешами паролей. Соль как раз и вводится, что бы подобные сервисы не работали. К тому же в базе порядка 1 млрд паролей что составляет лишь ничтожную долю всех возможных значений. Объём 2^128 значени хранить не способен никто. А вообще спасибо. Инетресно было почитать.
Март 2nd, 2009
Да, я как раз имел в виду то, что сервис по извлечению возможной комбинации не поможет, если применять соль. Все правильно
Март 2nd, 2009
novice, сорри. Значит я не правильно понял.
Я понял, что использование SHA обусловлено тем, что сервис по восстановлению md5 уже есть.
Июнь 3rd, 2009
День добрый.
Позволю себе добавить, чем еще SHA-1 лучше чем MD5.
Не так давно было обнаружено, что в алгоритме MD5 могут возникать коллизии (это когда один и тот же хэш может быть получен из разных строк), что небезопасно. Существуют группы хакеров, вычисляющих коллизии.
Добавляю Вас в RSS. Будем дружить
Июнь 4th, 2009
Денис Каратаев, коллизии возникают в любой хеш функции!
И, кстати, коллизию вычислить не сложно, достаточно использовать парадокс дня рождения. Но к сожалению это всё слабо применимо к реверсированию хеш-функций!
Июнь 5th, 2009
Польза sha1 и соли заключается и в том, что, если взломщик сумел подобрать коллизию, войти на сайт с другим паролем будет уже невозможно.
К слову, md5 уже взломали (вбейте в гугле md5 collision)
Июнь 5th, 2009
>К слову, md5 уже взломали (вбейте в гугле md5 collision)
да, я это и имел ввиду