Flask实现密码存储安全性
Flask
实现密码存储安全性
在互联网上网大多数用户在不同的网站上使用的是相同的密码,如果某个网站的密码以明文存储在数据库中,又不幸的被攻击者获取了数据库的访问权限,后果不堪设想,比如
CSDN
脱裤门事件.想要保护用户的密码,就不能使用明文存储密码,也不能用可从密文恢复原文的加密方式.这就需要使用哈希算法的单向加密方法对密码进行加密.
单向加密意味者数据被加密之后,就不可能通过密文反向计算
原文.但是哈希算法只是生成数据的摘要,所以相同的数据会生成相同的密文.
可以把密码的哈希值存储起来,然后在用户登录的时候,使用相同的哈希算法对输入的密码进行计算,并比较计算除的哈希值与存储在数据库中的哈希值是否相同.这样就算攻击者获取了数据库的访问权限,他们也不能得到用户的原密码.
哈希算法有多种,但是大多数都不安全,比如
MD5
和SHA1
.这写都纷纷被破解了.常用的是SHA-1
1.使用werkzeug
werkzeug.security
模块可以很方便的实现哈希值的计算,只需要2个函数,分别用于用户注册和用户验证阶段.
generate_password_hash(password,method=pbkdf2:sha1,salt_length=8)
:这个函数将原始密码作为输入,以字符串形式输出密码的哈希值.method
和``salt_length`的默认值能满足大多数需求
check_password_hash(hash,password)
:这个函数参数是哈希值和用户输入的密码,返回值为True
表示密码正确.比如:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import check_password_hash, generate_password_hash
# 1.初始化
app = Flask(__name__)
app.config.update({
'DEBUG': True,
'AUTO_TEMPLATES_RELOAD': True,
})
# 2.创建Flask路由
@app.route('/')
def index():
return "Hello World"
# 3.数据库
# 3.1 创建数据库连接
msg = 'mysql+pymysql://root:[email protected]:3306/flask_sqlalchemy_demo'
app.config.update({
'SQLALCHEMY_DATABASE_URI': msg,
'SQLALCHEMY_TRACK_MODIFICATIONS': False # 此项需要 flask-sqlalchemy显式支持
})
db = SQLAlchemy(app)
class Users(db.Model):
__tablename__ = 'users_test'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(45), nullable=False)
_password = db.Column(db.String(100), nullable=False)
# 设置完基本的模型后,为了前端的安全,这里的密码字段不使用 password
# 使用 _password 作为类的属性,这是类的私有属性,不允许访问.
# 映射一个 password 方法作为属性使用
def __init__(self, username, password,**kwargs):
self.username = username
self.password = password
# super(Users, self).__init__(**kwargs)
@property
def password(self):
return self._password
# 定义设置方法
@password.setter
def password(self, raw_password):
'''加密,存储hash值'''
self._password = generate_password_hash(raw_password)
@password.deleter
def password(self):
del self._password
# 定义解密方法
def check_password(self, password):
# 如果原始密码与加密后的密码相同,返回True
return check_password_hash(self._password, password)
# 创建表
# db.drop_all()
# db.create_all()
# 写入数据
user = Users(username='Jack',password='123456')
db.session.add(user)
db.session.commit()
# 检验
pw = '123456'
a=db.session.query(Users).filter(Users.id == 2).first()
print(a.check_password(pw))
if __name__ == '__main__':
app.run()定义了
property
之后,执行的self.password
默认会去执行@password.setter
方法,这样就可以把加密后的密码存储在数据库中,验证也是同样的.
2.使用Flask-Bcrypt
官网
根据官网的说法,
Bcrypt
被故意的设计低效和缓慢,使暴力破解变得更加困难,类似与werkzeug
,它也提供了2个方法.
generate_password_hash(password,rounds=None,prefix=None)
.用``bcrypt生成一个密码的散列值,
rounds加言的复杂度,默认是
12,
prefix指定算法的版本.需要注意的是
python3必须指定
decode('utf-8')`.check_password_hash(pw_hash,password)
:返回True/False
,第一个参数指定加密后的密码,第二个参数指定原始密码.安装:
$ pip install flask-bcrypt
使用
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
msg = 'mysql+pymysql://root:[email protected]:3306/flask_sqlalchemy_demo'
app.config.update({
'DEBUG': True,
'AUTO_TEMPLATES_RELOAD': True,
'SQLALCHEMY_DATABASE_URI': msg,
'SQLALCHEMY_TRACK_MODIFICATIONS': False # 此项需要 flask-sqlalchemy显式支持
})
db = SQLAlchemy(app)
# 为了避免命名空间冲突,命名为 flask_bcrypt
flask_bcrypt = Bcrypt(app)
class Users(db.Model):
__tablename__ = 'users_test'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(45), nullable=False)
_password = db.Column(db.String(100), nullable=False)
# 设置完基本的模型后,为了前端的安全,这里的密码字段不使用 password
# 使用 _password 作为类的属性,这是类的私有属性,不允许访问.
# 映射一个 password 方法作为属性使用
def __init__(self, username, password):
self.username = username
self.password = password
@property
def password(self):
return self._password
# 定义设置方法
@password.setter
def password(self, raw_password):
'''加密,存储hash值'''
self._password = flask_bcrypt.generate_password_hash(raw_password).decode('utf-8')
@password.deleter
def password(self):
del self._password
# 定义解密方法
def check_password(self, password):
# 如果原始密码与加密后的密码相同,返回True
return flask_bcrypt.check_password_hash(self._password, password)
# 创建表
db.drop_all()
db.create_all()
# 写入数据
user = Users(username='Jack', password='123456')
db.session.add(user)
db.session.commit()
# 检验
pw = '123456'
a = db.session.query(Users).filter(Users.id == 1).first()
print(a.check_password(pw))
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run()推荐使用
Bcrypt
,MD5,SHA
的算法速度太快了,会被破译.而Bcrypt
采用了一系列不同的Blowfish
加密算法,并引入了一个work factor
,这个工作因子可以让你决定这个算法的代价有多大.所以这个算法不会因为计算机CPU处理速度变快了,而导致算法的时间会缩短了.因为你可以增加work factor来降低其性能.那么Bcrypt到底有多慢?
如果和MD5比较,Bcrypt使用值为12的work factor加密,
Bcrypt
需要0.3秒,而MD5只需要一微秒(百万分之一秒)也就是说,如果说MD5加密的口令的只需要40秒就可以穷举完所有的可能的,而使用Bcrypt需要12年.