Прежде всего, важное предупреждение:
ПРЕДУПРЕЖДЕНИЕ : НИКОГДА хранить пароли пользователей. NEVER .В тот момент, когда ваш сервер будет взломан, у хакеров будет не только ваш сервер, но и ключ шифрования, и все пароли.
Вместо этого сохраните пароль хэши .См. Почему хеширование паролей считается таким важным? и Разница между хешированием пароля и его шифрованием .
В Python используйте passlib для обработки хэширования паролей.
Благодаря этому вы можете автоматически сохранять хэши паролей в вашей базе данных или выполнять любые другие преобразования данных, используя свойства, чтобы сделатьпреобразование.Я использую это в моих пользовательских моделях.
В следующем примере модели свойство password
фактически устанавливает для столбца password_hash
хешированное значение:
from passlib.context import CryptContext
PASSLIB_CONTEXT = CryptContext(
# in a new application with no previous schemes, start with pbkdf2 SHA512
schemes=["pbkdf2_sha512"],
deprecated="auto",
)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
# site-specific fields, like name, email, etc.
# password hash handling
# pbkdf2-sha512 is 130 characters short, but it never hurts to
# leave space for future growth of hash lengths.
password_hash = db.Column(db.String(256), nullable=False)
def __init__(self, password=None, password_hash=None, **kwargs):
if password_hash is None and password is not None:
password_hash = self.generate_hash(password)
super().__init__(password_hash=password_hash, **kwargs)
@property
def password(self):
raise AttributeError("User.password is write-only")
@password.setter
def password(self, password):
self.password_hash = self.generate_hash(password)
def verify_password(self, password):
return PASSLIB_CONTEXT.verify(password, self.password_hash)
@staticmethod
def generate_hash(password):
"""Generate a secure password hash from a new password"""
return PASSLIB_CONTEXT.hash(password.encode("utf8"))
Это позволяет мнеиспользуйте User(..., password=password)
, и он автоматически хеширует пароль для меня.Или измените пароль с помощью if new_password == new_password_again and some_user.verify_password(old_password): some_user.password = new_password
, не вспоминая, как снова хэшировать пароль.
Вы также можете использовать «скрытые» столбцы для хранения любых других данных, которые необходимо зашифровать при хранении, расшифровать при получении.Назовите атрибуты вашей модели начальным подчеркиванием _
, чтобы пометить их как закрытые API, а затем передайте реальное имя столбца объекту Column()
в качестве первого аргумента.Затем используйте объект свойства для обработки шифрования и дешифрования:
class Foo(db.Model):
__tablename__ = "foo"
id = db.Column(db.Integer, primary_key=True)
# encrypted column, named "bar" in the "foo" table
_bar = db.Column("bar", db.String(256), nullable=False)
@property
def bar(self):
"""The bar value, decrypted automatically"""
return decrypt(self._bar)
@bar.setter
def bar(self, value):
"""Set the bar value, encrypting it before storing"""
self._bar = encrypt(bar)
Это описано в руководстве по SQLAlchemy в разделе Использование дескрипторов и гибридов .Обратите внимание, что в этом случае нет смысла использовать hybrid_property
, поскольку ваша база данных не может шифровать и дешифровать на стороне сервера.
Если вы выполняете шифрование, хранит ваши ключи отдельно от вашихисходный код и убедитесь, что вы поворачиваете свои ключи (держите старые ключи под рукой, чтобы расшифровать старые данные, которые еще не были повторно зашифрованы), чтобы в случае взлома ключа вы по крайней мере могли его заменить.
Выберите хороший, надежный рецепт шифрования.Пакет cryptography
включает рецепт Fernet, см. другой мой ответ , чтобы узнать, как его использовать, затем перейдите на MultiFernet()
класс для управления вращением ключа.
Также ознакомьтесь с рекомендациями по управлению ключами .Криптографию очень легко ошибиться.