设计web程序时,人们往往会 高估数据库中用户信息的安全性,如果攻击者入侵了服务器获取了数据库,用户的安全就处在风险之中,这个风险比你想象的要大。大多数用户都在不同的网站使用相同的密码,因此即便不保存任何敏感信息,攻击者获得存储在数据库中的密码之后,也能访问用户在其他网站中的账户。
若要保证数据库中的密码安全,关键在于不能存储密码本身,而要存储密码的散列值。计算密码散列值的函数接收密码作为输入,使用一种或多种加密算法转换密码,最终得到一个和原始密码没有关系的字符序列。核对密码时,密码散列值可替代原始密码,因为计算散列值的函数是可复现的:只要输入一样,结果就一样。
>[danger]# 使用Werzeug实现密码散列
werzeug中的security模块能够很方便的实现密码散列值的计算,这一功能的实现只需两个函数,分别用在注册用户和验证用户阶段。
>[success]~~~
>generate_password_hash(password, method=pbkdf2:sha1, salt_length=8):
># 将原始密码作为输入,以字符串形式输出密码散列值,输出的值可保存在用户数据库中
>check_password_hash(hash, password):
># 数据库中存储的hash值与输入的密码经过计算后的散列值做对比,相同输出True
例如:
~~~
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
@property
def password(self): # 设置只写属性
raise AttributeError('password is not readable.')
@password.setter # 设置只写属性
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash,password)
~~~
>[danger]# 使用passlib实现密码散列
Passlib是一个用于Python 2和3的密码散列库,它提供了30多种密码散列算法的跨平台实现,以及用于管理现有密码散列的框架。它被设计用于广泛的任务,其中之一为多用户应用程序提供密码散列。
~~~
from passlib.apps import custom_app_context
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
@property
def password(self): # 设置只写属性
raise AttributeError('password is not readable.')
@password.setter
def password(self, password):
self.password_hash = custom_app_context.encrypt(password)
def verify_password(self, password):
return custom_app_context.verify(password, self.password_hash)
~~~
补充:生成散列值的内部方法举例
~~~
from passlib.hash import pbkdf2_sha256
>>> # generate new salt, and hash a password
>>> hash = pbkdf2_sha256.hash("toomanysecrets")
>>> hash
'$pbkdf2-sha256$29000$N2YMIWQsBWBMae09x1jrPQ$1t8iyB2A.WF/Z5JZv.lfCIhXXN33N23OSgQYThBYRfk'
>>> # verifying the password
>>> pbkdf2_sha256.verify("toomanysecrets", hash)
True
>>> pbkdf2_sha256.verify("randomsecrets", hash)
False
~~~