存储具有基本安全措施的密码的过程相当简单:
- 用盐对密码进行哈希处理
- 为每个用户/密码使用不同的盐
- 将带有哈希密码的盐存储在数据库中
- 当他们尝试登录时,通过相同的方法运行尝试过的 PW;比较结果。
如果他们输入了正确的密码,散列的密码将匹配。散列可以保护用户免受攻击,也可以防止看门人走过显示有members 表格的屏幕。
创建盐和散列密码
' salt size is 32 (0-31
Private Const SaltSize As Integer = 31
...
Dim dbPW As String = TextBox1.Text
Dim dbSalt = CreateNewSalt(SaltSize)
' eg: "dsEGWpJpwfAOvdRZyUo9rA=="
Dim SaltedPWHash As String = GetSaltedHash(dbPW, dbSalt)
' examples:
' using SHA256: bbKN8wYYgoZmNaG3IsQ2DPS2ZPIOnenl6i5NwUmrGmo=
' using SHA512:
' 0vqZWBIbOlyzL25l9iWk51CxxJTiEM6QUZEH1ph+/aNp+lk4Yf8NYv8RLhYtbqCNpOqO3y8BmM+0YWtbAhE+RA=="
将 PW 哈希和盐存储为用户记录的一部分。盐不是秘密,但当/如果用户更改密码时,请更改它。
比较登录尝试
' check if PW entered equals DB
Dim pwTry = TextBox2.Text
' hash the login attempt using the salt stored in the DB
Dim pwLogin = GetSaltedHash(pwTry, dbSalt)
' compare the hash of what they entered to whats in the DB:
If String.Compare(SaltedPWHash, pwLogin, False) = 0 Then
' okay!
Console.Beep()
End If
如果用户输入同一个PW,结果应该是同一个hash,就这么简单。哈希码并没有那么复杂:
哈希方法
Private Function GetSaltedHash(pw As String, salt As String) As String
Dim tmp As String = pw & salt
' or SHA512Managed
Using hash As HashAlgorithm = New SHA256Managed()
' convert pw+salt to bytes:
Dim saltyPW = Encoding.UTF8.GetBytes(tmp)
' hash the pw+salt bytes:
Dim hBytes = hash.ComputeHash(saltyPW)
' return a B64 string so it can be saved as text
Return Convert.ToBase64String(hBytes)
End Using
End Function
Private Function CreateNewSalt(size As Integer) As String
' use the crypto random number generator to create
' a new random salt
Using rng As New RNGCryptoServiceProvider
' dont allow very small salt
Dim data(If(size < 7, 7, size)) As Byte
' fill the array
rng.GetBytes(data)
' convert to B64 for saving as text
Return Convert.ToBase64String(data)
End Using
End Function
- 使用 GUID (
System.Guid.NewGuid.ToString) 之类的东西作为 salt 很诱人,但使用加密随机数生成器并不是那么难。
- 与散列密码一样,由于编码,返回字符串更长。
- 每次用户更改密码时创建一个新的盐。不要使用全局盐,它会破坏目的。
- 您还可以多次散列 PW。部分关键是如果/当受到攻击时,尝试所有各种组合需要很长时间。
- 这些函数非常适合
Shared / static 类成员。
另请注意,Kenneth 链接的文章非常值得一读。
请注意,the article 提到 The salt should be stored in the user account table alongside the hash 这并不意味着您必须在 DB 中有 Salt 列。您可以在链接的文章中看到以下内容:
Dim dbPW As String = TextBox1.Text
Dim dbSalt = CreateNewSalt(SaltSize)
' get the salted PW hash
Dim SaltedPWHash As String = GetSaltedHash(dbPW, dbSalt)
' store salt with the hash:
SaltedPWHash = String.Format("{0}:{1}", dbSalt, dbPW)
' salt + ":" + hashed PW now ready to store in the db
从散列密码中分离盐:
Dim SaltAndPWHash = rdr.Item("PWHash").ToString()
Dim split = SaltAndPWHash.Split(":"c) ' split on ":"
Dim Salt = split(0) ' element(0) == salt
Dim StoredPWHash = split(1) ' element(1) == hashed PW
这两个部分都需要:在 PW 中对尝试的日志进行哈希处理后,将其与 split(1) 进行比较。