Правильно храним пароли в БД

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

К сожалению, ошибки в скриптах и различные уязвимости иногда позволяют прочесть данные с помощью сформированных специальным образом 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-хэш, можно посмотреть, какие пароли по нему могут быть получены. Адрес этого сервиса - https://gdataonline.com/. Может быть есть еще какие-нибудь подобные сайты, но я не искал.

На сегодня все.
Всего доброго! :)





Читайте также:



14 Ответов на “Правильно храним пароли в БД”

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

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

    Кстати, насколько я знаю, Rainbow-таблицы (на которых основана работа того же GData) есть не только для md5, но и для sha1, так что он не намного безопаснее.

  2. miraz

    Спасибо за столь подробную информацию, пароль это неотъемлемая часть…..и главное что он был надежным!!

  3. novice

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

  4. А почему потребуется больше времени?
    Допустим, злоумышленник знает, что
    $hash = md5($salt.$password);
    и вытащил из базы
    $hash = ‘6547436690a26a399603a7096e876a2d';
    $salt = ‘bbb';
    Он будет перебирать варианты $password и смотреть, какой даст правильный хэш при таком алгоритме. В случае, если бы “соль” не использовалась, он перебирал бы такие же пароли и нашёл бы правильный за такое же число попыток. Или вы что-то другое имели в виду?

  5. novice

    Я имел в виду, что sha1($salt.sha1($pwd).$salt) должно длиться дольше, чем просто sha1($salt.$pwd)

  6. Igor

    Я обычно шифрую пароли сначала md5 потом sha1 + добавляю соль

  7. Вообще говоря сложность алгоритмов шифрования данных во многом определяется задачей. Например если мне нужно хранить пароль от профиля на форуме зачем его “солить”. Хранить md5($pwd) да и хватит.

  8. Написано всё верно. Не подкопаешься. :)
    Единственное что мне не понятно - это по поводу сервиса с md5 хешами паролей. Соль как раз и вводится, что бы подобные сервисы не работали. К тому же в базе порядка 1 млрд паролей что составляет лишь ничтожную долю всех возможных значений. Объём 2^128 значени хранить не способен никто. А вообще спасибо. Инетресно было почитать. :)

  9. novice

    Да, я как раз имел в виду то, что сервис по извлечению возможной комбинации не поможет, если применять соль. Все правильно :)

  10. novice, сорри. Значит я не правильно понял. :) Я понял, что использование SHA обусловлено тем, что сервис по восстановлению md5 уже есть. :)

  11. День добрый.
    Позволю себе добавить, чем еще SHA-1 лучше чем MD5.

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

    Добавляю Вас в RSS. Будем дружить :)

  12. Денис Каратаев, коллизии возникают в любой хеш функции! :) И, кстати, коллизию вычислить не сложно, достаточно использовать парадокс дня рождения. Но к сожалению это всё слабо применимо к реверсированию хеш-функций!

  13. Алекс

    Польза sha1 и соли заключается и в том, что, если взломщик сумел подобрать коллизию, войти на сайт с другим паролем будет уже невозможно.
    К слову, md5 уже взломали (вбейте в гугле md5 collision)

  14. >К слову, md5 уже взломали (вбейте в гугле md5 collision)

    да, я это и имел ввиду :)


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map