在 PHP 中创建安全密码哈希但检查 Access VBA
Posted
技术标签:
【中文标题】在 PHP 中创建安全密码哈希但检查 Access VBA【英文标题】:Creating secure password Hash in PHP but checking Access VBA 【发布时间】:2020-05-21 07:16:18 【问题描述】:我现在已经阅读了许多页面,并且正在考虑散列密码。
我创建了一个 MS Access 应用程序,它连接到 VPS 上的 mysql 后端。 我正在为用户使用强密码,只允许通过远程 MYSQL 允许来自静态 IPS 的连接,并且我正在使用带有证书和密钥的 SSL 连接到服务器。
我的下一步是确保密码经过哈希处理并正确保存,但这是我的困境:
在下载我的应用程序并使用创建的用户名和密码登录之前,在我的网站 (php) 上创建了用户帐户。我需要找到一种“相当强大但不是愚蠢到我现在无法使用它”的方法来保存这些密码。
我正在处理用户名和地址以及一些潜在的机密信息。
现在我的理解(而且我对这个散列的东西不熟悉)是我应该: - 为每个新密码创建一个随机 SALT - 为用户存储盐在表中 - 使用 bCrypt 或 sCrypt(SALT + PASS)之类的东西对其进行散列 - 额外的安全性我可以将 HMAC 与密钥一起使用(存储在不同的服务器上)
首先 - 我理解正确吗?
第二: - bCrpyt 和 sCrypt 在 ms Access 上的 VBA 上可用,因为我有用户通过这种方法登录,所以我不知道该去哪里。 - 我确实找到了一个要购买的软件包(cryptoApi 之类的),显然 cn 可以在 VBA 和 php 中访问这些...
这里有什么想法或建议吗? php 和 vba 显然都需要使用相同的系统。 从任何标准来看,我目前的 SHA1 都不够好!
【问题讨论】:
【参考方案1】:一些注意事项:
-
HMAC 用于消息身份验证,而不是散列密码。
BCrypt 或 SCrypt 未在 Windows API 中实现,而 SHA256/384/512 是。 SHA512 对于散列密码非常安全。
如果我们选择 SHA512,您可以使用我之前分享的散列方法 here。此代码使用 Windows CNG API,并列出了允许的散列算法here 以及相应的操作系统支持。一个电话就像HashString("SomePassword" & "SomeSalt")
一样简单。它返回一个字节数组,为方便起见,您可以将其转换为 Base64,但将其存储为二进制更有效。
您可以使用相同的 API 生成随机数(盐)。我已经概述了一种专注于整数的方法 here,但您可能希望生成随机字节,然后将字符串添加到这些字节。
另一个挑战是字符编码。 PHP 是 UTF-8,Access 是 UTF-16,并不真正做 UTF-8。如果将可能的字符限制为 ASCII,则可以在 VBA 中使用 StrConv
将字符串转换为 ANSI,并验证其中没有非 ASCII 字符。如果您想在 VBA 中将字符串转换为 UTF-8,则需要在散列之前使用另一个 API 调用 (WideCharToMultiByte
)。请注意,密码中的 unicode 是一种 UX 危害,因为某些操作系统将特殊字符表示为复合字符,而有些则不表示,这种差异会导致密码不匹配,因此有理由将其保留为 ASCII。
查看下面的哈希码:
Public Declare PtrSafe Function BCryptOpenAlgorithmProvider Lib "BCrypt.dll" (ByRef phAlgorithm As LongPtr, ByVal pszAlgId As LongPtr, ByVal pszImplementation As LongPtr, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptCloseAlgorithmProvider Lib "BCrypt.dll" (ByVal hAlgorithm As LongPtr, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptCreateHash Lib "BCrypt.dll" (ByVal hAlgorithm As LongPtr, ByRef phHash As LongPtr, pbHashObject As Any, ByVal cbHashObject As Long, ByVal pbSecret As LongPtr, ByVal cbSecret As Long, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptHashData Lib "BCrypt.dll" (ByVal hHash As LongPtr, pbInput As Any, ByVal cbInput As Long, Optional ByVal dwFlags As Long = 0) As Long
Public Declare PtrSafe Function BCryptFinishHash Lib "BCrypt.dll" (ByVal hHash As LongPtr, pbOutput As Any, ByVal cbOutput As Long, ByVal dwFlags As Long) As Long
Public Declare PtrSafe Function BCryptDestroyHash Lib "BCrypt.dll" (ByVal hHash As LongPtr) As Long
Public Declare PtrSafe Function BCryptGetProperty Lib "BCrypt.dll" (ByVal hObject As LongPtr, ByVal pszProperty As LongPtr, ByRef pbOutput As Any, ByVal cbOutput As Long, ByRef pcbResult As Long, ByVal dfFlags As Long) As Long
Public Function NGHash(pData As LongPtr, lenData As Long, Optional HashingAlgorithm As String = "SHA1") As Byte()
'Erik A, 2019
'Hash data by using the Next Generation Cryptography API
'Loosely based on https://docs.microsoft.com/en-us/windows/desktop/SecCNG/creating-a-hash-with-cng
'Allowed algorithms: https://docs.microsoft.com/en-us/windows/desktop/SecCNG/cng-algorithm-identifiers. Note: only hash algorithms, check OS support
'Error messages not implemented
On Error GoTo VBErrHandler
Dim errorMessage As String
Dim hAlg As LongPtr
Dim algId As String
'Open crypto provider
algId = HashingAlgorithm & vbNullChar
If BCryptOpenAlgorithmProvider(hAlg, StrPtr(algId), 0, 0) Then GoTo ErrHandler
'Determine hash object size, allocate memory
Dim bHashObject() As Byte
Dim cmd As String
cmd = "ObjectLength" & vbNullString
Dim Length As Long
If BCryptGetProperty(hAlg, StrPtr(cmd), Length, LenB(Length), 0, 0) <> 0 Then GoTo ErrHandler
ReDim bHashObject(0 To Length - 1)
'Determine digest size, allocate memory
Dim hashLength As Long
cmd = "HashDigestLength" & vbNullChar
If BCryptGetProperty(hAlg, StrPtr(cmd), hashLength, LenB(hashLength), 0, 0) <> 0 Then GoTo ErrHandler
Dim bHash() As Byte
ReDim bHash(0 To hashLength - 1)
'Create hash object
Dim hHash As LongPtr
If BCryptCreateHash(hAlg, hHash, bHashObject(0), Length, 0, 0, 0) <> 0 Then GoTo ErrHandler
'Hash data
If BCryptHashData(hHash, ByVal pData, lenData) <> 0 Then GoTo ErrHandler
If BCryptFinishHash(hHash, bHash(0), hashLength, 0) <> 0 Then GoTo ErrHandler
'Return result
NGHash = bHash
ExitHandler:
'Cleanup
If hAlg <> 0 Then BCryptCloseAlgorithmProvider hAlg, 0
If hHash <> 0 Then BCryptDestroyHash hHash
Exit Function
VBErrHandler:
errorMessage = "VB Error " & Err.Number & ": " & Err.Description
ErrHandler:
If errorMessage <> "" Then MsgBox errorMessage
Resume ExitHandler
End Function
Public Function HashBytes(Data() As Byte, Optional HashingAlgorithm As String = "SHA512") As Byte()
HashBytes = NGHash(VarPtr(Data(LBound(Data))), UBound(Data) - LBound(Data) + 1, HashingAlgorithm)
End Function
Public Function HashString(str As String, Optional HashingAlgorithm As String = "SHA512") As Byte()
HashString = NGHash(StrPtr(str), Len(str) * 2, HashingAlgorithm)
End Function
【讨论】:
哇,好吧,这让我大吃一惊:(所以我正在研究你所说的,我明白了,但不知道如何实现它。显然我可以创建这个哈希,例如。 “你好”给出了所有这些汉字。我可以用 PHP 创建一个随机盐并存储在表中,获取它并添加到密码,使用您的代码哈希为 SHA512。从这里我对 UTF 8 和 16 等感到迷茫。将以上对我的建议不起作用?我不需要在 VBA 中生成盐,只需在 PHP 中然后存储在表中。 如果你想走ASCII路线,你可以使用HashBytes(StrConv("Hello", vbFromUnicode))
。您可以使用在线字符串哈希器验证这一点。但是您需要确保密码中没有 unicode 字符(例如表情符号/汉字/等)。否则,您想将字符串从 UTF-16 转换为 UTF-8,我建议在单独的线程中询问。有很多关于保存为 UTF-8 的内容,但我找不到关于转换为 UTF-8 的内容,我能够回答这个问题,但我认为它值得自己提出问题,因为它是一个广泛适用的主题。
非常感谢@Erik 我不相信我会处理除了基本的澳大利亚键盘字符之外的任何东西,因为这将是澳大利亚唯一的应用程序
如果您不在 PHP 端进行验证,这可能是一个危险的假设,因为人们会做一些奇怪的事情(比如 Elon Musk 创建了一个帐户并想用他孩子的名字作为他的密码) ,或者有人决定在他们的密码中放一个便便表情符号是超安全的,因为没有人期望它)。请参阅this answer 了解如何在 PHP 中检查它。
@xShen 该代码需要不必要的 .Net 依赖,具有 COM 开销 + 来自通过后期绑定 COM 传递密码的不安全性,依赖于未记录的重载函数排序,并且不允许控制硬件加密提供商,我强烈建议不要使用它,而只使用 CNG 代码。使用 MSXML 将字节转换为十六进制只会增加对伤害的侮辱,这简直是荒谬的。该函数返回一个默认为二进制的哈希值。您可以稍后使用您选择的任何函数格式化哈希。以上是关于在 PHP 中创建安全密码哈希但检查 Access VBA的主要内容,如果未能解决你的问题,请参考以下文章
在 PHP 中匹配 128 个字符的密码哈希 - 使用 Ruby on Rails 加密