上一期的文章《我們應該如何保護使用者的密碼》裡我們介紹了bcrypt
相較於MD5
,SHA-1
…SHA-256
等雜湊演算法更適合用於做密碼的雜湊,原因就是bcrypt
演算法雜湊字串的速度遠遠慢於上面列舉的那些演算法。這樣即使整個使用者密碼庫被使用者盜用後想要通過彩虹表和暴力破解的方法猜測出使用者的密碼代價會非常高昂。今天的文章裡就主要來看一下bcrypt
雜湊的組成部分以及在Go
語言裡如何使用bcrypt
對密碼字串進行雜湊。
bcrypt雜湊字串的組成
bcrypt
雜湊由多個部分組成。這些部分用於確定建立雜湊的設定,從而可以在不需要任何其他資訊的情況下對其進行驗證。
上圖是一個bcrypt
雜湊的示例圖,其由四部分組成:
Prefix
說明了使用的bcrypt
的版本Cost
是進行雜湊的次數-數字越大生成bcrypt
的速度越慢,成本越大。同樣也意味著如果密碼庫被盜,攻擊者想通過暴力破解的方法猜測出使用者密碼的成本變得越昂貴。Salt
是新增到要進行雜湊的字串中的隨機字元(21.25個字元),所以使用bcrypt
時不需要我們在表裡單獨儲存Salt
。Hashed Text
是明文字串最終被bcrypt
應用這些設定雜湊後的雜湊文字。
另外無論什麼方法:每個密碼加單獨的鹽進行雜湊,使用bcrypt
進行雜湊等等,如果使用者使用非常簡單的密碼例如password
或123456
,還是能被猜測出來的,所以在使用者設定密碼時應該禁止他們輸入簡單的密碼。
Go語言使用bcrypt
bcrypt
的原理和實現都非常複雜,不過常用的程式語言都有實現bcrypt
的包讓我們直接使用,在Go
語言裡是通過golang.org/x/crypto/bcrypt
包提供bcrypt
相關功能給開發者使用的。
接下來我們在http_demo
專案裡演示一下使用bcrypt
做密碼雜湊和驗證的方法,首先我們需要安裝一下bcrypt
包
$ go get golang.org/x/crypto/bcrypt
bcrypt
包只提供了三個函式:
CompareHashAndPassword
用於比對bcrypt
雜湊字串和提供的密碼明文文字是否匹配。GenerateFromPassword
以給定的Cost
返回密碼的bcrypt
雜湊。如果給定的成本小於MinCost
,則將成本設定為DefaultCost
(10)。Cost
返回用於建立給定bcrypt
雜湊的雜湊成本。將來密碼系統為了應對更大的計算能力而增加雜湊成本時,該功能可以用於確定哪些密碼需要更新。
我們建立一個處理請求的Handler
程式,演示bcrypt
庫三個函式的功能
// ./handler/password_hashing.go
package handler
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"net/http"
)
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
func GetHashingCost(hashedPassword []byte) int {
cost, _ := bcrypt.Cost(hashedPassword) // 為了簡單忽略錯誤處理
return cost
}
func PassWordHashingHandler(w http.ResponseWriter, r *http.Request) {
password := "secret"
hash, _ := HashPassword(password) // 為了簡單忽略錯誤處理
fmt.Fprintln(w,"Password:", password)
fmt.Fprintln(w, "Hash: ", hash)
match := CheckPasswordHash(password, hash)
fmt.Fprintln(w,"Match: ", match)
cost := GetHashingCost([]byte(hash))
fmt.Fprintln(w,"Cost: ", cost)
}
增加Handler
程式的路由:
func RegisterRoutes(r *mux.Router) {
...
indexRouter := r.PathPrefix("/index").Subrouter()
indexRouter.HandleFunc("/password_hashing", handler.PassWordHashingHandler)
...
}
重啟http_demo
伺服器後訪問http://localhost:8000/index/password_hashing
即可得到如下結果:
Password: secret
Hash: $2a$14$Ael8nW7UF/En/iI7LGdyBuaIO8VREbL2CAShRN0EUQHqtmOHXh.XK
Match: true
Cost: 14
本文原始碼已經打包上傳,公眾號回覆gohttp13
即可獲得下載連結。如果覺得我的文章有收穫,請幫忙點”在看”分享給更多人。
本作品採用《CC 協議》,轉載必須註明作者和本文連結