bcrypt 可以加鹽的密碼比對

Posted in :

古時候的人誤以為資料庫很安全, 所以一開始密碼是放明碼.

後來的人意識到資料庫可能會把搬走, 所以密碼欄位內容改放 md5 hash 過的的值, 使用 md5 的好處是, 比對時可以是在資料庫的 sql 指令做比對.

md5 的好處是雖然比較難還原使用的一開始輸入的明碼, 資料被搬走後, 還是可以透過爆力的字串比對來取得使用者的明碼.

今天看到 tornado project 裡附的 blog 的 demo source code, 發現一個有趣的套件 bcrypt:
https://github.com/pyca/bcrypt/

實際使用bcrypt後, 缺點就是無法在資料庫裡做比對, 要先把 hash 過的字串, 拿出來在程式語言裡比對, 而且很酷的是, 使用同一個密碼, 每次產生出來的字串都長的不一樣, 當然這也是一個優點, 調整一下程式邏輯的順序就可以切換到 bcrypt 的比對了.

tornado 的 blog 的 demo source code:
https://github.com/tornadoweb/tornado/tree/stable/demos/blog


bcrypt 使用範例:

Usage

Password Hashing

Hashing and then later checking that a password matches the previous hashed password is very simple:

>>> import bcrypt
>>> password = b"super secret password"
>>> # Hash a password for the first time, with a randomly-generated salt
>>> hashed = bcrypt.hashpw(password, bcrypt.gensalt())
>>> # Check that an unhashed password matches one that has previously been
>>> # hashed
>>> if bcrypt.checkpw(password, hashed):
...     print("It Matches!")
... else:
...     print("It Does not Match :(")

tornado blog 使用的 “產生” hash 的 code.

hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(
    None,
    bcrypt.hashpw,
    tornado.escape.utf8(self.get_argument("password")),
    bcrypt.gensalt(),
)

tornado blog 使用的 “比對” 的 code.

password_equal = await tornado.ioloop.IOLoop.current().run_in_executor(
    None,
    bcrypt.checkpw,
    tornado.escape.utf8(self.get_argument("password")),
    tornado.escape.utf8(author.hashed_password),
)

說明: 比起 checkpw, haskpw 比較不會有問題, checkpw 的問題在比對的時候的是在 webserver 上比對, 現在大多數的 tornodo code 都寫成 async, 遇到要同步處理時就要 await, 但在 sqlite 中, 使用到 await 會掛掉, 錯誤訊息是:

# TypeError: object sqlite3.Cursor can't be used in 'await' expression

這些都還很容易處理, 獨立寫一個 get_hashed_password_by_account(user_account) 的 function 就可以了, 這個 function 等同於 check_account_exist(), 只差在會不會把 hashed password 回傳.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *