Станом на Python 3.4, hashlib
модуль у стандартній бібліотеці містить ключові функції виведення, якими є «призначені для безпечного хешування паролів» .
Тож використовуйте один із них, наприклад hashlib.pbkdf2_hmac
, із сіллю, утвореною за допомогою os.urandom
:
from typing import Tuple
import os
import hashlib
import hmac
def hash_new_password(password: str) -> Tuple[bytes, bytes]:
"""
Hash the provided password with a randomly-generated salt and return the
salt and hash to store in the database.
"""
salt = os.urandom(16)
pw_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
return salt, pw_hash
def is_correct_password(salt: bytes, pw_hash: bytes, password: str) -> bool:
"""
Given a previously-stored salt and hash, and a password provided by a user
trying to log in, check whether the password is correct.
"""
return hmac.compare_digest(
pw_hash,
hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
)
salt, pw_hash = hash_new_password('correct horse battery staple')
assert is_correct_password(salt, pw_hash, 'correct horse battery staple')
assert not is_correct_password(salt, pw_hash, 'Tr0ub4dor&3')
assert not is_correct_password(salt, pw_hash, 'rosebud')
Зверніть увагу, що:
- Використання 16-байтової солі та 100000 ітерацій PBKDF2 відповідають мінімальним числам, рекомендованим у документах Python. Подальше збільшення кількості ітерацій зробить ваші хеші повільнішими для обчислення, а отже, і більш безпечними.
os.urandom
завжди використовує криптографічно безпечне джерело випадковості
hmac.compare_digest
, який використовується в is_correct_password
, в основному є лише ==
оператором для рядків, але без можливості короткого замикання, що робить його несприйнятливим до синхронізації атак. Це, мабуть, насправді не надає жодної додаткової цінності для безпеки , але це також не зашкодить, тому я продовжив і використовував це.
Теорію щодо того, що робить хороший хеш пароля, та перелік інших функцій, придатних для хешування паролів, див. Https://security.stackexchange.com/q/211/29805 .
t_sha.digest() + salt
. Ви можете розділити сіль ще раз пізніше, коли розшифруєте солений хеш-пароль, оскільки знаєте, що розшифрований хешований пароль становить рівно 32 байти.