如何在Microsoft Access中使用PBKDF2加盐哈希密码?
在Microsoft Access中实现PBKDF2密码哈希的解决方案
嘿,我之前刚好帮客户处理过从无盐SHA1升级到带盐PBKDF2的Access数据库需求,给你几个在Access环境里完全可行的方案,都是我实际测试过的:
方案1:用VBA调用Windows CryptoAPI实现PBKDF2
Windows系统自带的CryptoAPI已经包含了PBKDF2的实现,我们可以通过VBA声明API函数来调用它。这种方案不需要依赖任何外部库,纯原生支持。
首先,在Access的VBA模块里添加以下声明和函数:
Option Explicit ' CryptoAPI声明 Private Declare PtrSafe Function CryptAcquireContext Lib "advapi32.dll" Alias "CryptAcquireContextA" _ (ByRef phProv As LongPtr, ByVal pszContainer As String, ByVal pszProvider As String, _ ByVal dwProvType As Long, ByVal dwFlags As Long) As Boolean Private Declare PtrSafe Function CryptGenRandom Lib "advapi32.dll" _ (ByVal hProv As LongPtr, ByVal dwLen As Long, ByVal pbBuffer As Byte) As Boolean Private Declare PtrSafe Function CryptDeriveKeyPBKDF2 Lib "advapi32.dll" Alias "CryptDeriveKeyPBKDF2" _ (ByVal hProv As LongPtr, ByVal pbPassword As Byte, ByVal cbPassword As Long, _ ByVal pbSalt As Byte, ByVal cbSalt As Long, ByVal dwIterations As Long, _ ByVal pbDerivedKey As Byte, ByVal cbDerivedKey As Long) As Boolean Private Declare PtrSafe Function CryptReleaseContext Lib "advapi32.dll" _ (ByVal hProv As LongPtr, ByVal dwFlags As Long) As Boolean ' 生成随机盐(16字节) Public Function GenerateSalt() As Byte() Dim salt(15) As Byte Dim hProv As LongPtr If CryptAcquireContext(hProv, vbNullString, vbNullString, 1, &H80000000) Then Call CryptGenRandom(hProv, 16, salt(0)) Call CryptReleaseContext(hProv, 0) End If GenerateSalt = salt End Function ' 生成PBKDF2哈希(用SHA256,迭代10000次,输出32字节哈希) Public Function GeneratePBKDF2Hash(password As String, salt As Byte()) As Byte() Dim hProv As LongPtr Dim passwordBytes() As Byte Dim hashBytes(31) As Byte passwordBytes = StrConv(password, vbFromUnicode) If CryptAcquireContext(hProv, vbNullString, vbNullString, 1, &H80000000) Then Call CryptDeriveKeyPBKDF2(hProv, passwordBytes(0), UBound(passwordBytes) + 1, _ salt(0), UBound(salt) + 1, 10000, hashBytes(0), 32) Call CryptReleaseContext(hProv, 0) End If GeneratePBKDF2Hash = hashBytes End Function ' 辅助函数:把字节数组转成十六进制字符串存储 Public Function BytesToHex(bytes() As Byte) As String Dim i As Long For i = 0 To UBound(bytes) BytesToHex = BytesToHex & Right("0" & Hex(bytes(i)), 2) Next i End Function ' 辅助函数:把十六进制字符串转回字节数组 Public Function HexToBytes(hexStr As String) As Byte() Dim bytes() As Byte ReDim bytes(Len(hexStr) \ 2 - 1) As Byte Dim i As Long For i = 0 To UBound(bytes) bytes(i) = Val("&H" & Mid(hexStr, i * 2 + 1, 2)) Next i HexToBytes = bytes End Function
使用流程:
- 注册新用户时:生成随机盐,对密码生成PBKDF2哈希,把盐(转成十六进制字符串)和哈希值(转成十六进制字符串)存入数据库的
PasswordSalt和PasswordHash字段。 - 用户登录时:从数据库取出该用户的盐,转成字节数组,用输入的密码生成PBKDF2哈希,对比生成的哈希和数据库里存储的哈希是否一致。
方案2:用VBA调用.NET的Rfc2898DeriveBytes类
如果你对.NET比较熟悉,也可以通过Access的COM互操作来调用.NET框架里的Rfc2898DeriveBytes类,这个类专门为PBKDF2实现做了封装,代码会更简洁。
步骤:
- 打开Access的VBA编辑器,点击
工具->引用,找到并勾选Microsoft .NET Framework 4.x(版本根据你的系统选择)。 - 添加以下VBA函数:
Option Explicit ' 生成16字节随机盐(转成Base64字符串存储) Public Function GenerateSalt() As String Dim rng As New System.Security.Cryptography.RNGCryptoServiceProvider Dim salt(15) As Byte rng.GetBytes(salt) GenerateSalt = Convert.ToBase64String(salt) End Function ' 生成PBKDF2哈希(SHA256,10000次迭代,32字节输出,转成Base64字符串存储) Public Function GeneratePBKDF2Hash(password As String, saltBase64 As String) As String Dim salt() As Byte salt = Convert.FromBase64String(saltBase64) Dim pbkdf2 As New System.Security.Cryptography.Rfc2898DeriveBytes(password, salt, 10000, System.Security.Cryptography.HashAlgorithmName.SHA256) Dim hash() As Byte hash = pbkdf2.GetBytes(32) GeneratePBKDF2Hash = Convert.ToBase64String(hash) End Function
这个方案的优点是代码更简洁,不需要自己处理复杂的API声明,缺点是需要确保目标机器安装了对应的.NET Framework版本。
密码迁移建议
因为你之前已经有一批用无盐SHA1存储的用户密码,直接替换会导致用户无法登录,推荐用渐进式迁移的方式:
- 在用户表中新增两个字段:
PasswordSalt(文本类型,存储Base64或十六进制的盐)和PasswordHashPBKDF2(文本类型,存储PBKDF2哈希值)。 - 保留原来的
OldPasswordSHA1字段暂时不删除。 - 用户登录时,先检查
PasswordHashPBKDF2是否为空:- 如果不为空:用PBKDF2的方式验证密码。
- 如果为空:用原来的SHA1方式验证密码,验证通过后,立即生成新的盐和PBKDF2哈希,更新到
PasswordSalt和PasswordHashPBKDF2字段。
- 等所有用户都完成迁移后,再删除旧的SHA1字段。
安全性注意事项
- 盐的长度至少16字节,确保每个用户的盐都是唯一的。
- 迭代次数建议设置在10000-50000之间,平衡安全性和性能(Access的VBA性能不算特别高,10000次是比较稳妥的选择)。
- 哈希算法选择SHA256或SHA512,不要再用SHA1了。
内容的提问来源于stack exchange,提问作者OofYeetMcGee
相关产品推荐
相关产品推荐

