【Flask】Flask實現密碼儲存安全性的兩種方式
密碼儲存安全性
網際網路上的大多使用者都會在不同的網站使用相同的密碼,如果某個網站把密碼以明文方式儲存在資料庫裡,又不幸地被攻擊者獲取了資料庫的訪問許可權,那後果不堪設想(比如號稱中國最大IT社群的某DN脫褲門事件,搞得我要單獨記一個密碼)
要想保護使用者的密碼,就不能明文儲存密碼,也不能用可從密文恢復原文的加密方式。那就要使用雜湊演算法的單向加密方法對密碼進行加密。單向加密意味著資料被加密之後,就不可能通過密文反向計算原文。但是雜湊演算法只是生成資料的摘要,所以相同的資料會生成相同的密文,版本控制工具Git就是通過SHA-1對檔案生成快照來進行內容變化追蹤
我們可以把密碼的雜湊值儲存起來,然後在使用者登入的時候,使用相同的雜湊演算法對輸入的密碼進行計算,並比較計算出的雜湊值與儲存在資料庫中的雜湊值是否相同。這樣就算攻擊者獲得了資料庫的訪問許可權,他們也得不到使用者的原密碼,不過可能會有其他隱私的洩露問題,這個只能靠相關網站的良心了,大家注意不要把自己的隱私儲存在網站上,永遠記得不要相信網際網路(的安全性)
雜湊演算法有很多種,但是大多數不安全,常用的比如MD5和SHA1。現在MD5已經不推薦使用了,隨便在網上搜一下就有各種“MD5線上破解”,如果還有網站用MD5加密,那這個網站已經沒人管了,死翹翹了。SHA1雖然被Google破解了,但是一般人還是別想了,所以SHA-1已經可以滿足需求了
給一個2017年2月23日Google發表的一篇破解SHA-1的論文給出的資料
- md5:只需要一個智慧手機30秒就破解了
- sha-1 shattered:110GPU 需要一年
- sha-1 bruteforce:12000000GPU 需要一年
使用Werkzeug
Werkzeug
中的security
模組可以很方便的實現雜湊值的計算,只需要兩個函式,分別用於使用者註冊和使用者驗證階段
generate_password_hash(password,method=pbkdf2:sha1,salt_length=8)
:這個函式將原始密碼作為輸入,以字串形式輸出密碼的雜湊值。method和salt_length的預設值能滿足大多數需求check_password_hash(hash,password)
:這個函式引數是雜湊值和使用者輸入的密碼。返回值為True表示密碼正確
下面例子是SQLAlchemy
的User
模型
from werkzeug.security import generate_password_hash, check_password_hash
class User(db.model):
password_hash = db.Column(db.String())
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
set_password()
方法在使用者註冊的時候使用,會呼叫generate_password_hash()
並將password
引數傳給它,將它的返回值儲存在列屬性password_hash
中
check_password()
方法在使用者驗證的時候使用,會呼叫check_password_hash()
並將資料庫儲存的雜湊值和使用者輸入的密碼傳給它,並返回它的返回值,如果是True
則表示密碼正確
使用Flask-Bcrypt
Bcrypt
被故意設計成計算起來低效而緩慢,從而使暴力破解變得更加困難,同樣也有兩個方法,方法名跟werkzeug.security
一樣
安裝Flask-Bcrypt
pip install Flask-Bcrypt
首先建立Bcrypt
物件,並加到app
物件中
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_name)
bcrypt.init_app(app)
然後讓User
物件使用Bcrypt
from app import bcrypt
class User(db.model):
password_hash = db.Column(db.String())
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
推薦使用Bcrypt
,MD5、SHA的演算法速度太快了,那為什麼會因為Bcrypt
慢就使用它?
因為Bcrypt
採用了一系列不同的Blowfish
加密演算法,並引入了一個work factor
,這個工作因子可以讓你決定這個演算法的代價有多大。所以這個演算法不會因為計算機CPU處理速度變快了,而導致演算法的時間會縮短了。因為你可以增加work factor
來降低其效能
那麼Bcrypt
到底有多慢?
如果和MD5比較,Bcrypt
使用值為12
的work factor
加密“cool”。Bcrypt
需要0.3秒,而MD5只需要一微秒(百萬分之一秒)。也就是說,如果說MD5加密的口令的只需要40秒就可以窮舉完所有的可能的,而使用Bcrypt
需要12年