儲存密碼該用什麼雜湊演算法?
| | | 0 | |
開始之前,先同步幾點觀念:
- 系統保存密碼時,應該用雜湊而非加密,雜湊是個單向函數,文字可以轉成雜湊,但無法從雜湊還轉回原始文字。如此可確保任何人(包含系統管理者)都無法解密拿到密碼明碼,但卻又可以判斷輸入文字是否跟密碼一致。
換言之,若有某個系統的「忘記密碼」居然能把上回設定的密碼寄給你,該系統的安全防護很可能是酥皮等級,別用,快逃~
延伸閱讀:密碼要怎麼儲存才安全?該加多少鹽?-科普角度 - 所讀的暴力破解密碼只適用「攻擊者已進展到從資料庫、記憶體、檔案系統取得雜湊過的密碼內容,可帶回家泡杯茶慢慢破解的情境」,若還擋在線上登入階段,則有多因素驗證、錯誤三次鎖帳號、連續錯誤鎖 IP... 等諸多手段可以有效保護密碼。
雜湊被偷走的情況多嗎?先不提駭客常會設法拿到雜湊打開大門,站方整批流出也時有所聞(例如:2021 Thingiverse 就曾流出一批 "unsalted SHA-1 or bcrypt password hashes"),硬說不必擔心暴力破解多少有點鴕鳥心態。 - 我們常用的 SHA256 雜湊用於檔案比對、數位簽章已足夠,但拿來保存密碼便顯得不夠安全。要儲存密碼,建議還是要用專屬雜湊演算法。(註:更好的做法是別自建帳密管理系統,依賴 Google、Microsfot、Github、FB... 等的身分驗證機制)
延伸閱讀:六位複雜密碼用 SHA256 面對 5080 擋不過一分鐘
因此,當需要用雜湊保存密碼,選用高破解難度的密碼用雜湊演算法,會比用 SHA256 這類通用雜湊更安全。目前密碼學界普遍認可的幾種密碼用雜湊有以下幾種:
| 演算法 | 發展年份 | 記憶體需求 | 抗攻擊能力 | 效能特性 | 適用場景 |
|---|---|---|---|---|---|
| Argon2id | 2015 | 可調整 (19-46 MiB) | 最強,抗 GPU/ASIC/側通道攻擊 | CPU 與記憶體平衡 | 新系統、高安全需求 |
| bcrypt | 1999 | 固定 4 KB | 良好,但易受 FPGA 攻擊 | 穩定、跨平台一致 | 一般網路應用、舊系統 |
| scrypt | 2009 | 可調整 (8-128 MiB) | 強,特別抗硬體攻擊 | 高記憶體消耗 | 加密貨幣、高安全系統 |
| PBKDF2 | 2000 | 低 | 最弱,易受 GPU/ASIC 攻擊 | 最快但不利密碼儲存 | FIPS 合規需求 |
bcrypt 的主要問題在 4 KB 記憶體需求太低,易被 FPGA (Field-Programmable Gate Array,現場可程式化邏輯閘陣列)之類的硬體用高速平行運算暴力破解。scrypt 對記憶體有較高需求(8 ~ 128MiB),比 bcrypt 更能抵抗專屬硬體(ASIC)攻擊,但存在觀測記憶體存取進行側通道攻擊 (Side-Channel Attack) 的風險。PBKDF2 的抗攻擊能力相對偏弱,除非規格寫死,一般不建議再使用。
相較之下,2015 年密碼雜湊大賽(PHC)的獲勝者 - Argon2id,結合了 Argon2i (抗側通道攻擊) 與 Argon2d (抗 GPU 攻擊)的優點,是目前最全面性、最推薦的密碼雜湊首選。
認識了當代主流密碼雜湊,沒實際跑個程式總覺少了點什麼?那就來用 .NET 實際玩一回吧。
經訐估,使用純 C# 開發的開源程式庫 Konscious.Security.Cryptography.Argon2,是最多人使用的 Argon2id 程式庫,其支援 .NET Standard 1.3、.NET Framework 4.6+、.NET 6.0+,可適用大部分專案環境。以下是個簡單範例:
using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;
byte[] salt = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
byte[] hash = ComputeArgon2idHash("password", salt);
Console.WriteLine("Hash1 =" +BitConverter.ToString(hash));
byte[] newHash = ComputeArgon2idHash("password", salt);
Console.WriteLine("Hash2 =" + BitConverter.ToString(newHash));
// 驗證雜湊時不要直接比較,使用固定時間比較函式防止側通道攻擊
bool isEqual = CryptographicOperations.FixedTimeEquals(hash, newHash);
Console.WriteLine($"Hashes are equal: {isEqual}");
static byte[] ComputeArgon2idHash(string password, byte[] salt)
{
var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password))
{
DegreeOfParallelism = 8,
MemorySize = 46 * 1024,
Iterations = 3,
Salt = salt
};
return argon2.GetBytes(32);
}
實測結果如下,加鹽 (Salt) 能讓同一個密碼產生不同的雜湊值,防止攻擊者使用彩虹表 (Rainbow Table)預先算好可能密碼的雜湊值直接比對,實際應用時記得 Salt 要跟雜湊值一起保存。

而由程式邏輯可知 Salt 長度、DegreeOfParallelism、MemorySize、Iterations 均會影響破解難度。RFC 9106 及 OWASP 有參考值:
- Salt: 長度 8 到 2^32 - 1 Bytes,16 Bytes 為常用值
- DegreeOfParallelism:1 ~ 2^24
- MemorySize:至少 46 MiB、建議 64 MiB
- Iterations:3 (或 MemorySize 1GiB 配 Iteration 1)
練演完畢。
Explains secure password storage using hashing, compares common algorithms, and demonstrates Argon2id hashing in .NET with recommended parameters.
Comments
Be the first to post a comment