2 encryption sqlalchemy flask python-3.x
我有一个用户表,它有一个支付密码。我决定加密所有这些信息。每个 API 检索或插入新数据,它总是像这样调用一个函数来加密密码。
from cryption import encrypt, decrypt
enc_password = encrypt(password)
data = User(id=id, password=enc_password)
db.add(data)
db.commit()
Run Code Online (Sandbox Code Playgroud)
由于我在 API 中对其进行了加密,因此它看起来是多余的。此外,有时我忘记添加加密代码,这会导致严重错误。所以,我想知道我是否可以在模型上执行此操作而不是执行此操作。如果我可以在插入数据之前进行加密并在查询级别返回数据时进行解密,那就太好了。
首先,一个重要的警告:
警告:永远不要存储用户的密码。从来没有。一旦您的服务器遭到入侵,黑客不仅会拥有您的服务器,还会拥有加密密钥以及所有密码。
相反,存储密码哈希。请参阅为什么密码散列被认为如此重要?和散列密码和加密它的区别。
在 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"))
Run Code Online (Sandbox Code Playgroud)
这让我可以使用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)
Run Code Online (Sandbox Code Playgroud)
这在 SQLAlchemy 手册的Using Descriptors and Hybrids下有介绍。请注意,hybrid_property在这种情况下使用 a 没有意义,因为您的数据库无法在服务器端加密和解密。
如果您进行加密,请将您的密钥与您的源代码分开,并确保您轮换您的密钥(保留旧密钥以解密尚未重新加密的旧数据),这样如果密钥被泄露,您至少可以代替它。
选择一个好的、值得信赖的加密方法。该cryptography软件包包含 Fernet 配方,请参阅我的另一个答案以获取有关如何使用它的建议,然后升级到MultiFernet()该类以管理密钥轮换。
此外,请阅读关键管理最佳实践。密码学很容易出错。
| 归档时间: |
|
| 查看次数: |
896 次 |
| 最近记录: |