Начиная с 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 байта.