Я изучаю Flask с помощью замечательного учебника Мигель Гринберг. В этой главе он предлагает хранить хэш пароля пользователя, а не сам пароль по соображениям безопасности. Используемые функции: generate_password_hash
, check_password_hash
. Но даже если вы вызовете generate_password_hash
с той же строкой, вы можете получить разные хеш-значения:
>>> from werkzeug.security import generate_password_hash
>>> generate_password_hash('foo')
'pbkdf2:sha256:50000$E4Mg0BEy$c8db80b3ddefad78a93eaa47b22da5ce04adb969913b00545302cbf23501fdbb'
>>> generate_password_hash('foo')
'pbkdf2:sha256:50000$UCXVV09c$fe38b6099a0059957e283f2e4706fdbf01ef6e762b1070116df17867aa04e053'
Тогда как же работает check_password_hash
, если одна и та же строка может иметь сколько угодно хеш-значений?
Пароли хешируются с помощью соли, которая представляет собой псевдослучайную строку букв и цифр. Соль будет разной каждый раз, когда вы запускаете generate_password_hash()
. Следовательно, полученный хеш тоже будет другим.
Это сделано для того, чтобы хакеры не могли просто угадать хэш общих паролей. Например, хэш «pass1234» каждый раз будет одинаковым. Однако хеш «pass1234 + salt» каждый раз будет отличаться. Ваша база данных должна хранить хэш, а также соль (что важно, а не пароль в виде обычного текста). Это минимизирует ущерб, причиненный утечкой информации об учетных записях пользователей.
В случае Flask и werkzeug возвращаемое значение generate_password_hash()
имеет вид: method$salt$hash
(вы можете увидеть два символа $
на предоставленном вами снимке экрана). Поэтому в следующий раз, когда вы проверите пароль в открытом виде по хешу, вы получите его со значением соли из generate_password_hash()
и посмотрите, соответствует ли оно хеш-значению.
В Python Flask поле пароля таблицы user
хранится в $6$rounds=656000$JW9ctR1i8kLbiFAZ$HymY4FLhinJbiyShgd1BvZI.iOb6IwaXn0/rdNvmZfim2zsoP1FcPlUG.KX5Idl0wajXChSztnr5z1yRmwb.R/
, как я могу проверить, соответствует ли это значение простому тексту пароля
Then how does the check_password_hash work if the same string can have as many > hash values as it like?
Упрощенное решение может быть таким:
user_id| raw_password | hash_value
user1 | 'foo' | same_hashed_value
user2 | 'foo' | same_hashed_value
Как вы, возможно, знаете, здесь один и тот же пароль дает такое же хеш-значение, которое
уязвим для атаки по словарю. Таким образом, мы можем добавить случайное значение (salt
) к
смягчить этот вид атаки, как показано в следующем решении.
сгенерировать хеш-процесс:
user_id | raw_password | password_with_salt | hash_value
user1 | 'foo' | 'foo#salt123' | different_hash_1
user2 | 'foo' | 'foo#saltABC' | different_hash_2
В этом случае hash_value и связанное с ним salt_value могут храниться в базе данных. В противном случае, concat (hash_vaule
, salt_value
) как одна строка для хранения, что эквивалентно.
проверить хеш-процесс:
1) Получить из базы данных значение salt_value и hash_value. 2) Добавьте salt_value к заданному паролю и хешируйте его, используя ту же хеш-функцию. 3) Сравните хэш данного пароля с хешем из базы данных. Если они совпадают, пароль правильный. В противном случае пароль неверный.
Это описано на странице Flask Соленые пароли и более подробно на Википедия. Ключевое слово: соль.