vlambda博客
学习文章列表

Flask实现密码存储安全性

10329

Flask实现密码存储安全性

在互联网上网大多数用户在不同的网站上使用的是相同的密码,如果某个网站的密码以明文存储在数据库中,又不幸的被攻击者获取了数据库的访问权限,后果不堪设想,比如CSDN脱裤门事件.

想要保护用户的密码,就不能使用明文存储密码,也不能用可从密文恢复原文的加密方式.这就需要使用哈希算法的单向加密方法对密码进行加密.

单向加密意味者数据被加密之后,就不可能通过密文反向计算

原文.但是哈希算法只是生成数据的摘要,所以相同的数据会生成相同的密文.

可以把密码的哈希值存储起来,然后在用户登录的时候,使用相同的哈希算法对输入的密码进行计算,并比较计算除的哈希值与存储在数据库中的哈希值是否相同.这样就算攻击者获取了数据库的访问权限,他们也不能得到用户的原密码.

哈希算法有多种,但是大多数都不安全,比如MD5SHA1.这写都纷纷被破解了.常用的是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年.