使用者的密碼不得使用明文儲存於資料庫,避免資料庫管理員看的到密碼
同時密文也不可以使用可逆向解密的演算法,避免資料庫外洩後被解出來
為了達成上述兩個目的,會使用 hash 類的演算法,破壞可逆性後儲存
傳統的hash演算法 (如 md5) 已於2016被認定為不安全的 hash 演算法了,坊間有多個 md5 線上比對的網站,可以用下列的網站先產生md5,再拿去對照的網站測試結果,就會發現弱密碼幾乎都可以被反向解回來
md5 之後較新的演算法是 sha 演算法,可以搭配加鹽的方式避免碰撞攻擊,但 sha1 也已經在2017年被驗證可以被碰撞攻擊了,所以至少要使用 sha256 以上
但sha256也有其他問題,因為電腦運算速度一直提升,難保有一天電腦效能增加,sha256也被攻破
PBKDF2 演算法是一種實現 密鑰延伸(key stretching) 類型的演算法,這類型的演算法是為了對抗電腦運算速度太快,避免密碼被暴力破解的解決方案。
PBKDF2會先使用 hash 類型的演算法,將 hash 的結果當成hash 演算的鹽,重新再跑一次 hash,反覆遞迴運算。
因此只要將遞迴 hash 的次數調升,就可以輕鬆地讓運算速度降低,減緩運算速度快的破解時間。
http://vegeee-csharp.blogspot.com/2019/05/password-hash-using-pbkdf2-with-hmac.html?m=1
sha = 不可逆雜湊,加密速度快,但運算位數少時容易被碰撞攻擊
pdbdf2 = 可以把演算法加鹽並多跑n次遞回的演算法,速度慢
pbkdf2 + sha = 把sha演算法跑很多次,變成慢速不可逆演算法
也可以點此觀看執行結果範例
https://dotnetfiddle.net/JQX1AK
using System;
using System.Security.Cryptography;
namespace pbkdf2_sha2
{
class PasswordHash
{
private const int SaltByteSize = 32;
private const int HashByteSize = 32;
private const int Iterations = 4096;
private static string GetSalt()
{
var cryptoProvider = new RNGCryptoServiceProvider();
byte[] b_salt = new byte[SaltByteSize];
cryptoProvider.GetBytes(b_salt);
return Convert.ToBase64String(b_salt);
}
public static string GetPasswordHash(string password)
{
string salt = GetSalt();
byte[] saltBytes = Convert.FromBase64String(salt);
byte[] derived;
using (var pbkdf2 = new Rfc2898DeriveBytes(
password,
saltBytes,
Iterations,
HashAlgorithmName.SHA512))
{
derived = pbkdf2.GetBytes(HashByteSize);
}
return string.Format("{0}:{1}:{2}", Iterations, Convert.ToBase64String(derived), Convert.ToBase64String(saltBytes));
}
public static bool VerifyPasswordHash(string password, string hash)
{
try
{
string[] parts = hash.Split(new char[] { ':' });
byte[] saltBytes = Convert.FromBase64String(parts[2]);
byte[] derived;
int iterations = Convert.ToInt32(parts[0]);
using (var pbkdf2 = new Rfc2898DeriveBytes(
password,
saltBytes,
iterations,
HashAlgorithmName.SHA512))
{
derived = pbkdf2.GetBytes(HashByteSize);
}
string new_hash = string.Format("{0}:{1}:{2}", Iterations, Convert.ToBase64String(derived), Convert.ToBase64String(saltBytes));
return hash == new_hash;
}
catch
{
return false;
}
}
}
}
pbkdf2 使用的是 Rfc 2898 規範,他的套件是Rfc2898DeriveBytes,雖然最小支援度是寫dotnet Framework 4.6,但要升級到dotnet Framework 4.7.2以上才能在直接寫SHA-512這種方式挑選要採用的雜湊演算法
參考 https://kknews.cc/zh-tw/tech/aeoml8j.html
PBKDF2 確實有很大的效果,但對於硬體破解,卻無任何對抗措施。
因為 PBKDF2 只是對原函數簡單封裝,多執行幾次而已。如果原函數不能對抗硬體,那麼套一層 PBKDF2 同樣也不能。
儘管單次 PBKDF 不能被拆解,但可以要求多次 PBKDF,並且互相沒有依賴。這樣多線程就能派上用場了。
例如我們對 PBKDF 進行封裝,要求執行 4 次完全獨立的計算,最後再將結果融合到一起:
function Parall(Password, Salt, ...)
-- 該部分可被並行 --
for i = 0 .. 4
DK[i] = PBKDF(Password, Salt + i, ...)
------------------
return Hash(DK)
這樣,我們即可開啟 4 個線程,同時計算這 4 個 PBKDF。
現在就能用 1 秒的時間,獲得之前 4 秒的強度!攻擊者破解時,成本就增加了 4 倍。
bcrypt
scrypt
Argon2
https://blog.csdn.net/chenfanglincfl/article/details/46994393
https://riptutorial.com/zh-TW/csharp/example/10258/密碼哈希的pbkdf2
內含各種演算法破解速度比較圖表
https://blackie1019.github.io/2019/02/21/Secured-Password-with-PBKDF2-on-C/
aes可逆加密演算法
https://www.cnblogs.com/zxbzl/archive/2013/03/04/2942939.html
資安