SQLAlchemy: вызывать функцию и всегда сохранять возвращаемое значение в таблице - PullRequest
0 голосов
/ 24 июня 2019

У меня есть таблица пользователей, и у нее есть пароль для оплаты. Я решил зашифровать всю эту информацию. Каждый API извлекает или вставляет новые данные, он всегда вызывает функцию для шифрования пароля следующим образом.

  from cryption import encrypt, decrypt
  enc_password = encrypt(password)

  data = User(id=id, password=enc_password)
  db.add(data)
  db.commit()

Поскольку я шифрую это в API, оно выглядит избыточным. Кроме того, иногда я забыл добавить код шифрования, и это вызывает критические ошибки. Итак, мне интересно, могу ли я сделать это на моделях вместо этого. Если я смогу выполнить шифрование перед вставкой данных и расшифровать при возврате данных на уровне запроса, это будет хорошо.

1 Ответ

2 голосов
/ 24 июня 2019

Прежде всего, важное предупреждение:


ПРЕДУПРЕЖДЕНИЕ : НИКОГДА хранить пароли пользователей. 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() класс для управления вращением ключа.

Также ознакомьтесь с рекомендациями по управлению ключами .Криптографию очень легко ошибиться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...