使用基本安全措施存储密码的流程非常简单:
- 对密码进行哈希处理并加盐
- 为每个用户/密码使用不同的盐
- 将盐与哈希密码一起存储在数据库中
- 当用户尝试登录时,通过相同的方法运行尝试的密码并进行比较。
如果他们输入了正确的密码,则哈希密码将匹配。哈希保护用户免受攻击,同时也保护清洁工从显示成员表的屏幕旁边经过时无意中窥视到密码。
创建盐和哈希密码
Private Const SaltSize As Integer = 31
...
Dim dbPW As String = TextBox1.Text
Dim dbSalt = CreateNewSalt(SaltSize)
Dim SaltedPWHash As String = GetSaltedHash(dbPW, dbSalt)
将密码哈希值和盐作为用户记录的一部分存储。 盐不是秘密,但在用户更改密码时更改它。
比较登录尝试
Dim pwTry = TextBox2.Text
Dim pwLogin = GetSaltedHash(pwTry, dbSalt)
If String.Compare(SaltedPWHash, pwLogin, False) = 0 Then
Console.Beep()
End If
如果用户输入相同的密码,应该得到相同的哈希值,就这么简单。哈希代码并不是非常复杂:
哈希方法
Private Function GetSaltedHash(pw As String, salt As String) As String
Dim tmp As String = pw & salt
Using hash As HashAlgorithm = New SHA256Managed()
Dim saltyPW = Encoding.UTF8.GetBytes(tmp)
Dim hBytes = hash.ComputeHash(saltyPW)
Return Convert.ToBase64String(hBytes)
End Using
End Function
Private Function CreateNewSalt(size As Integer) As String
Using rng As New RNGCryptoServiceProvider
Dim data(If(size < 7, 7, size)) As Byte
rng.GetBytes(data)
Return Convert.ToBase64String(data)
End Using
End Function
- 使用类似GUID(
System.Guid.NewGuid.ToString
)作为盐看起来很诱人,但是使用密码随机数生成器并不难。
- 与哈希密码类似,由于编码方式的不同,返回字符串较长。
- 每当用户更改密码时,请创建一个新的盐。不要使用全局盐,这样做会使目的失效。
- 您也可以将密码哈希多次。关键部分在于使其需要长时间才能尝试所有各种组合,以防止攻击。
- 这些函数是
Shared
/static
类成员的理想候选者。
还要注意,由Kenneth链接的文章也值得一读。
请注意,文章提到:盐应存储在用户帐户表中,并与哈希值一起存储
。这并不意味着您必须在数据库中有一个Salt
列。链接的文章中展示了以下内容:
Dim dbPW As String = TextBox1.Text
Dim dbSalt = CreateNewSalt(SaltSize)
Dim SaltedPWHash As String = GetSaltedHash(dbPW, dbSalt)
SaltedPWHash = String.Format("{0}:{1}", dbSalt, dbPW)
将密码哈希值中的盐分离出来:
Dim SaltAndPWHash = rdr.Item("PWHash").ToString()
Dim split = SaltAndPWHash.Split(":"c)
Dim Salt = split(0)
Dim StoredPWHash = split(1)
你需要两部分:在你对尝试登录的密码进行哈希处理后,将其与split(1)
进行比较。