資料庫裡賬號的密碼,需要怎樣安全的存放?—— 密碼雜湊(Password Hash)
最早在大學的時候,只知道用 MD5 來存使用者的賬號的密碼,但其實這非常不安全,而所用到的雜湊函式,深入挖掘,也發現並不簡單……
一、普通的 Hash 函式
雜湊(雜湊)函式是什麼就不贅述了。
1、不推薦
RC4, MD4, MD5, SHA-0, SHA-1, DES, 2DES 等
2、推薦
SHA-2(SHA-256, SHA-384, SHA-512)、SHA-3、Blake2 等
美國國家標準和技術協會(NIST)宣佈,2010 年後開始逐步取消 SHA-1 作為安全雜湊演算法的資格,取而代之的是其更強大的變異演算法:SHA-224、SHA-256、SHA-384 和 SHA-512。無論是否遵循 NIST 的標準,至少使用 SHA-256 演算法加密密碼總是好的。
二、應對普通雜湊容易被破解的策略
就像攻與矛的互相增強,雜湊函式哪怕用到 SHA-3 以上,都還是有被輕易破解的風險。於是我們有其他額外的辦法來解決這個問題。
1、加鹽(salt)
加鹽就是對目標欄位雜湊前,拼接上另一個欄位(salt)。
注:鹽值加到欄位之前較為普遍。
加鹽對防彩虹表很有效。
注意點:
- 鹽不能太短
- 鹽不能重複使用(否則一破解,所有的都遭殃)
- 鹽隨機變化(例如,雖使用者名稱不重複,但使用者名稱不能拿來當鹽)
鹽的本質是將
無差別攻擊
轉化為針對性攻擊
。
1.1、【拓展】針對 salt 的另一種做法 —— HMAC
HMAC
(Keyed-Hashing for Message Authentication)其實也是一種特殊的加鹽,只是這個 salt 用更安全的金鑰代替了。
具體介紹可以看我之前一篇:《破解另一家網站的反爬機制 & HMAC 演算法》
2、慢雜湊
高階的顯示卡(GPU)和定製的硬體可以每秒進行數十億次雜湊計算,因此這類攻擊依然可以很高效。為了降低攻擊者的效率,我們可以使用慢雜湊,即迭代進行很多次雜湊運算。
那麼迭代多少次比較安全呢?來自 NIST 官方的建議:
- 2000 年 9 月,建議迭代一千次
- 2015 年 - 2018年,建議迭代一萬次
- 2017 年 6 月,建議迭代十萬次
三、密碼雜湊函式(Password Hash)
密碼雜湊函式(Password Hash)
可以用來應對普通雜湊容易被破解的問題(也用到了上面所提到的兩個策略)。
下面列舉的順序是按照時間順序,安全程度和推薦指數也逐級遞增。
1、PBKDF2
比較老,很少有人用了,略。
2、Bcrypt
這是我司目前用的。(不過有過時的隱患,建議換掉)
(1)介紹
bcrypt 是由 Niels Provos 和 DavidMazières 基於 Blowfish 密碼設計的密碼雜湊函式,於 1999 年在 USENIX 上提出。
bcrypt 函式是 OpenBSD 和其他系統(包括某些 Linux 發行版,例如 SUSE Linux)的預設密碼雜湊演算法。
(2)使用(Node.js)
安裝:npm i bcryptjs
bcryptjs
跟 C++ 的 bcrypt 相容,但因為是純 JavaScript 編寫的,因此速度較慢(約 30%)。
用法:
Sync 方法(Async 方法略):
const bcryptjs = require('bcryptjs');
// 1、生成 安全因子
const salt = bcrypt.genSaltSync(10);
// 2、執行 雜湊函式
const password = bcryptjs.hashSync(plainPassword, bcryptjs.genSaltSync(salt));
// 另一種方法:快速執行
const SALT_FACTOR = 10;
const password = bcryptjs.hashSync(plainPassword, bcryptjs.genSaltSync(SALT_FACTOR));
// 3、比較是否相等
bcryptjs.compareSync(plainPassword, password);
注:程式碼裡出現的安全因子
,值的大小決定了雜湊函式會有多慢。(即慢雜湊)
3、Scrypt
沒用過,略。
4、Argon2
(1)介紹
2013 年 NIST(美國國家標準與技術研究院)邀請了一些密碼學家一起,舉辦了密碼雜湊競賽 PHC(Password Hashing Competition)
。Argon2 在 2015 年 7 月贏得了冠軍。
大賽列出了參賽演算法可能面臨的攻擊手段:
- 雜湊演算法破解(原值還原、雜湊碰撞等);
- 查詢表/彩虹表攻擊;
- CPU 優化攻擊;
- GPU、FPGA、ASIC 等專用硬體攻擊;
- 旁路攻擊;
(2)使用(Node.js)
1、準備
You can skip this section if the prebuilt binaries work for you.
You MUST have a node-gyp global install before proceeding with install, along with GCC >= 5 / Clang >= 3.3. On Windows, you must compile under Visual Studio 2015 or newer.
node-argon2 works only and is tested against Node >=10.0.0.
---
OSX
To install GCC >= 5 on OSX, use homebrew:
$ brew install gcc
Once you've got GCC installed and ready to run, you then need to install node-gyp, you must do this globally:
$ npm install -g node-gyp
Finally, once node-gyp is installed and ready to go, you can install this library, specifying the GCC or Clang binary to use:
$ CXX=g++-6 npm install argon2
NOTE: If your GCC or Clang binary is named something different than g++-6, you'll need to specify that in the command.
2、安裝
npm i argon2
3、使用
const argon2 = require('argon2');
(async () => {
try {
// const hash = await argon2.hash("password");
// 更多選項(以下都是預設值)
const hash = await argon2.hash("password", {
type: argon2.argon2i,
hashLength: 32, // 雜湊函式輸出的位元組長度(請注意,生成的雜湊是使用Base64編碼的,因此長度將增加約1/3)
timeCost : 3, // 時間成本是雜湊函式使用的通過次數(迭代次數)
memoryCost: 2 ** 16, // 預設 4096(單位 KB,即 4MB)
parallelism :1, //用於計算雜湊值的執行緒數量。每個執行緒都有一個具有memoryCost大小的記憶體池
})
console.log("hash", hash)
const is = await argon2.verify(hash, "password")
console.log("is", is) // true
} catch (err) {
console.error("err", err)
}
})()
(3)引數
1、type:
argon2d
更快且對GPU攻擊具有高度抵抗力,這對於加密貨幣很有用argon2i
速度較慢且可以抵禦權衡攻擊,因此首選用於密碼雜湊和金鑰派生argon2id
是上述內容的混合組合,可以抵抗GPU和權衡攻擊
因為我們是用於密碼的 hash,用預設的 argon2i 即可。
2、(慢)雜湊相關引數
① memoryCost
記憶體開銷,它定義了記憶體的使用情況
好的起點是 0.75 *(RAM / number_of_users) 起步。
② parallelism
並行程度,它定義了執行緒的數量
最佳起點是核心數。
③ timeCost
時間開銷,它定義了執行的時間
建議在系統上執行它,並確定與記憶體和處理器使用時間限制相匹配的最大引數。
如前所述,本質是在安全性和可用性之間取得平衡。
3、其他引數
salt:預設值是未設定,將生成加密安全的隨機鹽。
saltLength:預設16。
version:您不應更改此設定,因為最新版本更強大。
5、密碼雜湊是如何解決普通雜湊容易被破解的問題
上面介紹了 二、應對普通雜湊容易被破解的策略 ,我們可以看看密碼雜湊是如何運用並符合這些策略的。
(1)針對 salt
密碼雜湊使用 CSPRNG(Cryptographically Secure Pseudo-Random Number Generator)密碼學安全偽隨機數生成器
生成鹽。
CSPRNG 是加密安全(Cryptographically Secure)
的,(加密安全的意思即)意味著用它產生的隨機數更加隨機,且不可預測。
普通的計算機隨機數演算法並不是很隨機。
注:鹽值本身就在存在於雜湊後的字串中(其實還可能包括版本、慢雜湊迭代次數等),當呼叫跟明文比對的方法時,模組內部會提取出鹽值進行驗證。
(2)針對 慢雜湊
Bcryoy 的安全因子和 Argon2的 timeCost 引數,都是針對慢雜湊的配置。
6、結論
我司使用的 Bcrypt 其實在今年(2020年),已經不安全了,推薦至少使用 Scrypt,有條件上 Argon2。
四、常見問題
問1:我用我自己實現雜湊演算法,不用公開現成的,越古怪越好,壞人不就猜不到了嗎?
答:不建議。
首先介紹下密碼學上的柯克霍夫原則
(Kerckhoffs's principle,也稱為柯克霍夫假說、公理、或定律),由奧古斯特·柯克霍夫在 19 世紀提出:即使密碼系統的任何細節已為人悉知,只要密匙(key,又稱金鑰或祕鑰)未洩漏,它也應是安全的。資訊理論的發明者克勞德·夏農則改成說:“敵人瞭解系統”,這樣的說法則稱為夏農箴言
。
基於這個原則:
- 1、你自己實現的再古怪,畢竟你不是密碼專家,很難確保不被壞人破解(可能自己實現後看似複雜,實際更容易破解了)。
- 2、如果自己包含雜湊演算法的程式碼洩露,它很脆弱,難保不會被壞人破解。
問2:既然現在都是 https,前端傳給後端的明文密碼,就懶得加雜湊了,可以嗎?
還是建議前端也進行雜湊(雖然前端的雜湊演算法容易暴露)。不要漏掉任何一個環節。
五、實操
Dropbox 公司曾公開分享過自己對使用者賬號的密碼加密的策略,使用了三層加密:
1、password
即明文密碼。
2、SHA512
在 bcrypt 前做 SHA512,是因為有些 bcrypt 實現會把雜湊值長度截至 72 位元組,從而降低了密碼的熵值,而有的則允許變長密碼,這樣容易受到 DoS 攻擊。使用 SHA512 雜湊可以得到固定長度的 512 位元組雜湊值,避免了上述的兩個問題。
”而有的則允許變長密碼,這樣容易受到 DoS 攻擊“,這句話我不是很理解,待寫。
3、bcrypt
上面說過,不贅述了。
4、AES256
AES256 會用到金鑰,俗稱胡椒粉(pepper)。金鑰需要被單獨儲存,最好儲存在外部系統:如物理上隔離的服務端、甚至特殊的硬體裝置(如 YubiHSM) 。
這裡的 AES256 也可以用 HMAC 代替。不過前者安全性更好些。