Go Web 程式設計--使用 bcrpyt 雜湊使用者密碼

KevinYan發表於2020-04-13

上一期的文章《我們應該如何保護使用者的密碼》裡我們介紹了bcrypt相較於MD5SHA-1SHA-256等雜湊演算法更適合用於做密碼的雜湊,原因就是bcrypt演算法雜湊字串的速度遠遠慢於上面列舉的那些演算法。這樣即使整個使用者密碼庫被使用者盜用後想要通過彩虹表和暴力破解的方法猜測出使用者的密碼代價會非常高昂。今天的文章裡就主要來看一下bcrypt雜湊的組成部分以及在Go語言裡如何使用bcrypt對密碼字串進行雜湊。

bcrypt雜湊字串的組成

bcrypt雜湊由多個部分組成。這些部分用於確定建立雜湊的設定,從而可以在不需要任何其他資訊的情況下對其進行驗證。

上圖是一個bcrypt雜湊的示例圖,其由四部分組成:

  • Prefix說明了使用的bcrypt的版本
  • Cost是進行雜湊的次數-數字越大生成bcrypt的速度越慢,成本越大。同樣也意味著如果密碼庫被盜,攻擊者想通過暴力破解的方法猜測出使用者密碼的成本變得越昂貴。
  • Salt是新增到要進行雜湊的字串中的隨機字元(21.25個字元),所以使用bcrypt時不需要我們在表裡單獨儲存Salt
  • Hashed Text是明文字串最終被bcrypt應用這些設定雜湊後的雜湊文字。

另外無論什麼方法:每個密碼加單獨的鹽進行雜湊,使用bcrypt進行雜湊等等,如果使用者使用非常簡單的密碼例如password123456,還是能被猜測出來的,所以在使用者設定密碼時應該禁止他們輸入簡單的密碼。

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 協議》,轉載必須註明作者和本文連結

公眾號:網管叨bi叨 | Golang、PHP、Laravel、Docker等學習經驗分享

相關文章