我对确保用户身份验证的最佳方式有疑问。
我遇到了一个加密 后端中的用户密码的Web应用程序。但是,使用相同密钥对所有密码进行加密。 此外,此密钥在后端是“硬编码”。
他们(应用程序开发人员)声称这是非常安全的。不过我有疑虑。我相信这种方法可能会导致两个问题:
加密密码的原因是为了避免在未经授权的数据库访问时访问密码。但是,如果您将密钥存储在同一服务器中,则他们也可以获得密钥。
相同的密码会产生相同的加密值,因此攻击系统会更容易。
我的问题如下:
我的说法是对的吗?如果真的那么不安全,我应该警告他们可能的威胁吗?
使用hash + salt方法的优缺点是什么?
谢谢!
答案 0 :(得分:4)
我不确定你是否可能错误地将加密和散列混合在一起。如果用户的密码是加密而不是哈希,那么攻击者有可能在数据泄露的情况下窃取所有用户密码。
在身份验证方面,您似乎有许多因素需要考虑。首先,任何散列都应该在后端完成,而不是在前端。在前端散列仍然容易受到哈希攻击。
一些开发人员采用双哈希方法,在前端对密码进行哈希处理,然后在后端重新哈希。我认为这是不必要的,前端密码应该由HTTPS层(TLS)覆盖,但是需要讨论。
首先,在解释如何安全地存储和验证用户之前,让我们先澄清两个关键术语。
您指定用户的密码是加密的,而不是哈希。加密函数的作用是将输入(用户密码)以1对1的方式映射到输出(加密密码),这意味着可逆。
这意味着如果黑客获得加密密钥(私钥)的访问权限,他们可以轻松地反转整个过程。
相反,用户的密码应该是服务器端的哈希。为什么?因为您可以通过比较两个哈希来检查它们是否匹配,而无需存储该值的纯文本表示。
再一次,你可能会问,“为什么”?好吧,因为散列函数是单向的,这意味着纯文本值不能被反转(好吧,它们很难),我不会详细介绍。
用户密码绝不应以纯文本形式存储在Web服务器的任何部分。相反,您应该存储用户的哈希值。当用户尝试登录时,您通过HTTPS / TLS安全地接收其纯文本密码,对其进行哈希处理,如果两个哈希匹配,则对用户进行身份验证。
因此数据库表可能如此:
+--------------------------------------+
| ID | Username | Password Hash |
+--------------------------------------+
| 1 | foo | $2a$04$/JicM |
| 2 | bar | $2a$04$cxZWT |
+--------------------------------------+
现在让我们以Alice和我们的服务器为例。不要过于字面地获取数据。
Alice使用她的凭据发送登录请求,该凭据首先通过我们的安全传输层:
{username: "foo", password: "bar"} -> TLS() -> ZwUlLviJjtCgc1B4DlFnK -> | Server |
我们的服务器接收到它,然后使用它的证书密钥来解密它:
ZwUlLviJjtCgc1B4DlFnK -> KEY() -> {username: "foo", password: "bar"} -> Web Application
大!我们的凭据已经安全通过,现在是什么?哈希密码并与我们在数据库中得到的内容进行比较。
BCRYPT('bar') -> $2a$04$/JicM
if ($2a$04$/JicM == user.get_password_hash) {
authenticate();
}
else {
return status_code(401);
}
我们现在能够对用户进行身份验证,存储不可逆的哈希值,而不会存储纯文本值。这应该已经回答了你的第一个和第二个问题。
答案 1 :(得分:0)
是的,你的分析是正确的,这是不安全的。
任何正式审计肯定都会失败,例如: PCI-DSS。开发商/运营商可能认为这些账户提供的资产价值不大,因此他们没有必要提供这样的保护,但是他们仍然有责任照顾他们的客户 - 而且大多数人都会对不同的网站/服务使用相同的密码。
它确实为用户“恢复”密码提供了一种手段,而不会产生过期OTP的复杂性 - 但是邮寄明文密码会进一步破坏安全性。
实际上,即使攻击者只能访问加密的密码数据(特别是如果它包含已知的加密值/不使用初始化向量),也可以导出加密密钥。