尝试登录的安全性(防止时间攻击)

本文关键字:时间 攻击 登录 安全性 | 更新日期: 2023-09-27 17:50:53

我遇到了一个安全问题。请看下面的伪代码

public static bool TryLogin(string email, string password)
{
    if (UserExists(email)) // here is the problem
        return false;
    var hash = GetRealPasswordHash(email);
    var hash2 = GetHash(email, password);
    return SlowEquals(hash, hash2);
}
你们中的一些人可能已经看到了这个洞。攻击者可以通过网络执行时间攻击——检测返回答案的速度,并基于此检测数据库中是否存在用户!知道这一点后,攻击者现在可以检查他已经知道存在的用户的各种密码!

如果你没有看到问题或者不认为这是一个问题(这是针对哈希的,但类比问题在这里):

比较"length-constant"中的哈希值时间保证攻击者无法在在线系统中提取密码的哈希值使用定时攻击,然后离线破解。

检查两个字节(字符串)序列是否相同的标准方法同样的方法是比较第一个字节,然后第二个,然后第三个,等等......一旦你发现一个字节对两个都不相同字符串,你知道它们是不同的,可以返回一个负数立即响应。如果你能穿过两根弦找到任何不同的字节,你就知道字符串是相同的和可以返回一个正结果。这意味着比较两个字符串可以花不同的时间取决于有多少字符串匹配。

例如,字符串"xyzabc"answers"abcxyz"会立即看出第一个字符不同吗也不会去检查字符串的其他部分。另一方面手,当琴弦"aaaaaaaaaaB"answers";aaaaaaaaaaZ"进行比较,比较算法扫描"a"之前确定字符串不相等。

这似乎是不可能运行一个定时攻击一个网络。然而,它已经做到了,并且已经被证明是实用。

在这里你可以看到文章

当我检测到一个用户不存在时,我应该怎么做呢?我当然应该进行更多的计算,但是在这里什么是好的技术呢?你们在这种情况下用什么?

尝试登录的安全性(防止时间攻击)

解决方案很简单,无论用户是否存在,您都遵循相同的代码路径。

public static bool TryLogin(string email, string password)
{
    bool userExists = UserExists(email);
    var hash = GetRealPasswordHash(email);
    var hash2 = GetHash(email, password);
    return SlowEquals(hash, hash2) && userExists;
}

现在您需要确保GetRealPasswordHash为有效电子邮件和非有效电子邮件花费相同的时间,并为非有效电子邮件返回"假"哈希(Guvante在他的评论中提到的"假"用户的密码哈希)。

这确保GetHashSlowEquals都仍然使用有效数据调用,但由于userExists为假,您忽略了它们的结果。

但是我也同意Guvante的第二点,任何正常的系统都会在多次无效密码尝试后锁定用户帐户。这可以防止针对用户登录的暴力破解/字典攻击。只有在一段时间过去或用户调用并验证其身份之后,您才能解锁登录时的锁定。