經典 ASP.NET 使用 FormsAuthentication.HashPasswordForStoringInConfigFile 進行密碼雜湊驗證。本文示範舊版寫法並說明為何應遷移至現代演算法。
private void LoginButton_Click(object sender, EventArgs e)
{
string sql = "SELECT PasswordHash FROM Users WHERE Username=@User";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["Conn"].ConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("@User", UserIdBox.Text);
conn.Open();
object dbHashObj = cmd.ExecuteScalar();
if (dbHashObj == null) { Result.Text = "使用者不存在"; return; }
string dbHash = dbHashObj.ToString();
// 使用者輸入密碼 → SHA1 雜湊 → 與資料庫比對
string inputHash = FormsAuthentication.HashPasswordForStoringInConfigFile(PasswordTextBox.Text, "SHA1");
if (inputHash == dbHash)
{
FormsAuthentication.RedirectFromLoginPage(UserIdBox.Text, true);
}
else
{
Result.Text = "密碼錯誤";
}
}
}
[Obsolete],Core/5+ 已移除。| 演算法 | .NET 實作 | 特點 | 建議參數 |
|---|---|---|---|
| PBKDF2 (Rfc2898DeriveBytes) | 內建 System.Security.Cryptography | 標準化、可調整迭代次數 | HMAC-SHA256, 100,000+ 迭代, 16+ 位元組 Salt |
| BCrypt | NuGet BCrypt.Net-Next | 內建 Salt、自適應成本因子 | Work Factor 12+ (2^12 迭代) |
| Argon2id | NuGet Konscious.Security.Cryptography.Argon2 | 記憶體硬化、抗 GPU/ASIC | Time=3, Memory=64MB, Parallelism=4 |
| ASP.NET Core Identity | 內建 PasswordHasher<TUser> | PBKDF2 + 隨機 Salt + 版本化 | 直接使用 HashPassword/VerifyHashedPassword |
public static string HashPassword(string password, int iterations = 100000)
{
using (var rng = RandomNumberGenerator.Create())
{
byte[] salt = new byte[16];
rng.GetBytes(salt);
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
byte[] hash = pbkdf2.GetBytes(32); // 256-bit
// 格式:iterations.salt.hash (Base64)
return $"{iterations}.{Convert.ToBase64String(salt)}.{Convert.ToBase64String(hash)}";
}
}
}
public static bool VerifyPassword(string password, string stored)
{
var parts = stored.Split('.');
if (parts.Length != 3) return false;
int iterations = int.Parse(parts[0]);
byte[] salt = Convert.FromBase64String(parts[1]);
byte[] hash = Convert.FromBase64String(parts[2]);
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
byte[] testHash = pbkdf2.GetBytes(32);
return CryptographicOperations.FixedTimeEquals(hash, testHash); // 定時比較防時序攻擊
}
}
密碼儲存安全性關係用戶資產與企業聲譽,**請立即停用 SHA1/MD5,採用 PBKDF2/BCrypt/Argon2**。