Golang每日一庫之bcrypt

始識發表於2023-04-27

官方檔案: https://pkg.go.dev/golang.org/x/crypto/bcrypt

前言

之前講過JWT Token https://www.cnblogs.com/zichliang/p/17303759.html JWT呢是資訊是經過數字簽名的,因此可以被驗證和信任。
然後今天就來說說密碼學,我們在做鑑權 做使用者處理時 會把密碼儲存到資料庫中,但是這個密碼我們肯定不能明文去儲存,如果這個資料庫連結一旦被別人拿到
那後果是不堪設想的。不僅僅是為了防止系統管理員或者DBA等公司人員獲得使用者的密碼,也是防止被駭客拖庫產生更大的資訊洩露。
如果駭客透過不法手段獲取了服務的資料庫儲存資訊,盜取裡面的內容,從而直接獲得明文密碼,那麼影響就會很大。
所以我們的密碼一般透過幾種方式去加密儲存

  1. MD5
    其實個人覺得MD5加密不太好,因為MD5是不加鹽的,雖然是不可逆的,但是駭客其實會針對常見的一些密碼,生成彩虹表。
    彩虹表是什麼呢?
    是用於加密雜湊函式逆運算的預先計算好的表,常用於破解加密過的密碼雜湊(維基百科)
    所以相對的感覺安全等級不是很夠。

  2. SHA1及其他
    SHA-1基於MD5,MD5又基於MD4
    SHA-1是由美國標準技術局(NIST)頒佈的國家標準,是一種應用最為廣泛的Hash函式演演算法,也是目前最先進的加密技術,被政府部門和私營業主用來處理敏感的資訊。
    這個缺點個人認為和 MD5一樣。

  3. hmacsha
    我之前也寫過相應的文章[https://www.cnblogs.com/zichliang/p/16653303.html] 裡面有相應的hmasha加密
    HMAC是金鑰相關的雜湊運算訊息認證碼(Hash-basedMessageAuthenticationCode),HMAC運算利用雜湊演演算法,以一個金鑰和一個訊息為輸入,生成一個訊息摘要作為輸出。
    HMAC是需要一個金鑰的。所以,HMACSHA1也是需要一個金鑰的,而SHA1不需要。

  4. CRC
    CRC的全稱為CyclicRedundancyCheck,中文名稱為迴圈冗餘校驗。它是一類重要的線性分組碼,編碼和解碼方法簡單,檢錯和糾錯能力強,在通訊領域廣泛地用於實現差錯控制。實際上,除資料通訊外,CRC在其它很多領域也是大有用武之地的。例如我們讀軟盤上的檔案,以及解壓一個ZIP檔案時,偶爾會碰到“BadCRC”錯誤,由此它在資料儲存方面的應用可略見一斑。

  5. 還有很多加密方式這裡就不一 一贅述了...

本文介紹一種加密方式 bcrypt

概述

bcrypt是一個由美國電腦科學家尼爾斯·普羅沃斯(Niels Provos)以及大衛·馬齊耶(David Mazières)根據Blowfish加密演演算法所設計的密碼雜湊函式,於1999年在USENIX中展示[1]。實現中bcrypt會使用一個加鹽的流程以防禦彩虹表攻擊,同時bcrypt還是適應性函式,它可以藉由增加迭代之次數來抵禦日益增進的電腦運算能力透過暴力法破解。

由bcrypt加密的檔案可在所有支援的作業系統和處理器上進行轉移。它的口令必須是8至56個字元,並將在內部被轉化為448位的金鑰。然而,所提供的所有字元都具有十分重要的意義。密碼越強大,資料就越安全。

除了對資料進行加密,預設情況下,bcrypt在刪除資料之前將使用隨機資料三次覆蓋原始輸入檔案,以阻撓可能會獲得計算機資料的人恢復資料的嘗試。如果您不想使用此功能,可設定禁用此功能。

具體來說,bcrypt使用美國密碼學家保羅·柯切爾的演演算法實現。隨bcrypt一起釋出的原始碼對原始版本作了略微改動。

以上內容來自於wiki維基百科 >>>> https://zh.wikipedia.org/wiki/Bcrypt

bcrypt基本介紹

其實簡單來說
bcrypt就是一種加鹽的單向Hash,不可逆的加密演演算法,同一種明文(plaintext),每次加密後的密文都不一樣,而且不可反向破解生成明文,破解難度很大
而我們熟知的另一種不可逆的加密演演算法
md5 是不加鹽的單向Hash,不可逆的加密演演算法,同一個密碼經過hash的時候生成的是同一個hash值,在大多數的情況下,有些經過md5加密的方法將會被破解。

Bcrypt生成的密文是60位的。而MD5的是32位的。
總的來說,BCrypt比MD5更安全,但加密更慢。
各有優缺點吧。

這裡推薦個網站可以完成 bcrypt的加密 我們來嚐嚐鮮。

https://www.bejson.com/encrypt/bcrpyt_encode/

安裝

這裡找遍了全網好像也沒找到github地址。並且也沒有什麼安裝的教程。

go get -u golang.org/x/crypto/bcrypt

cost常量分類

const (
        // 傳遞給GenerateFromPassword的最小允許開銷
	MinCost     int = 4  
        // 傳遞給GenerateFromPassword的最大允許開銷
	MaxCost     int = 31
        // 如果將低於MinCost的cost傳遞給GenerateFromPassword,則實際設定的cost
	DefaultCost int = 10 
)

使用

這裡我們直接看官方寫好的測試用例,可能需要 ...(你懂的)
https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.8.0:bcrypt/bcrypt_test.go

GenerateFromPassword 生成一個hash密碼

GenerateFromPassword以給定的代價返回密碼的bcrypt雜湊。如果給定的cost小於MinCost,則該cost將被設定為DefaultCost。
GenerateFromPassword不接受長度超過72位元組的密碼,這是bcrypt操作的最長密碼

password, _ := bcrypt.GenerateFromPassword([]byte("123"), bcrypt.DefaultCost)
fmt.Println(string(password))

結果如下
第一次

$2a\(10\)SNRLHrG.ExJHKfR8LihSLOqAJOu/hCpP0ARhwoKvsduxv5xMXkl4u
第二次
$2a\(10\)Np1EBVQ9DZXMvIUkT7Y2P.cA0psEmW2SAVJYcCDqDDN8TsASo7aZm

注: 每次結果都不一樣 因為這不是MD5加密,會透過加鹽來完成不可逆的加密

Cost方法 返回給定的cost

Cost返回用於建立給定雜湊密碼的雜湊成本。將來,當密碼系統的雜湊成本需要增加以適應更大的計算能力時,這個功能允許人們確定需要更新哪些密碼。
簡單來說 返回上文的 bcrypt.DefaultCost

cost, _ := bcrypt.Cost([]byte("$2a$10$XgLBtSfJsrBd.liLOYWddOYWYWboBUAlKmivcSwq647C3vTNUOVMO"))
fmt.Println(cost)

結果如下

10

CompareHashAndPassword 對比明文密碼和雜湊密碼

CompareHashAndPassword,將返回的雜湊密碼與其明文版本進行比較。

password, _ := bcrypt.GenerateFromPassword([]byte("123"), bcrypt.DefaultCost)
fmt.Println(string(password))

// 可以解析出上文
cost, _ := bcrypt.Cost([]byte("$2a$10$XgLBtSfJsrBd.liLOYWddOYWYWboBUAlKmivcSwq647C3vTNUOVMO"))
fmt.Println(cost)

err := bcrypt.CompareHashAndPassword(password, []byte("123"))
if err != nil {
	fmt.Println("密碼驗證錯誤", err)
}
fmt.Println("密碼驗證成功>>>", nil)

結果

$2a\(10\)ANuBn8FthHbgfYir4v65AOvdtqoR3xjZ0G8duN5ynH1Vm0h3yUF/G
10
密碼驗證成功>>>

bcrypt某些錯誤型別

  • type HashVersionTooNewError byte

使用 建立雜湊時從 CompareHashAndPassword 返回的錯誤 比此實現更新的 bcrypt 演演算法。
func (hv HashVersionTooNewError) Error() string 呼叫error返回字串

  • type InvalidCostError int

型別 無效cost錯誤
func (ic InvalidCostError) Error() string 呼叫error返回字串

  • type InvalidHashPrefixError byte

型別無效雜湊字首錯誤
當雜湊以“$”以外的內容開頭時,從 CompareHashAndPassword 返回的錯誤
func (ih InvalidHashPrefixError) Error() string 呼叫error返回字串

相關文章