看起来 一段时间,Unix系统上的登录实用程序仅在存在有效用户名时计算哈希值;这打开了一个安全漏洞,允许进行定时攻击,因为用户可以通过生成散列密钥进行比较所需的时间来确定何时找到用户名。
这对桌面应用程序有意义,但它对Web应用程序也有意义吗?我倾向于这样做,但这种修复是必要的吗?
例如,在Django auth模块中:
class MyBackend(ModelBackend):
def authenticate(self, email=None, password=None):
try:
user = User.objects.get(email=email)
return user if user.check_password(password) else None
except User.DoesNotExist:
User().check_password(password) # is this line necessary?
return None
额外的哈希计算是否适合这种情况?如果我在auth呼叫上使用速率限制,这是否会降低像这样的定时攻击的可能性?
原定时攻击, Tenex公司 攻击,与散列无关 - 它通过定位密码使其跨越页面边界导致虚拟内存缓存未命中。攻击者可以尝试定位一系列密码,以便只有第一个字符位于第一页中,并且当验证花费足够长的时间以便发生缓存未命中时,它会知道第一个字符匹配。攻击者可以为密码中的每个字符重复此操作。
除非攻击者能够控制内存中输入的细粒度定位,否则,对密码的定时攻击不是问题,而是任何超线性的秘密检查算法w.r.t.秘密的长度(> = O(秘密长度))可能泄漏有关密码长度的信息。
如果你小心比较密码中的所有字符而不管成功与否,那么你也可以击败攻击:
boolean match = true;
for (int i = 0; i < min(salted_from_db_length, salted_from_user_length); ++i) {
if (salted_from_db[i] != salted_from_user[i]) {
match = false;
//break; // Stopping early leaks info.
}
}
match = salted_from_db_length == salted_from_user_length && match;
您应该进行测试,以确保编译器优化不会将时序漏洞放回代码中。
注意,术语“定时攻击”也用于其他上下文,这些可能会影响Web应用程序。例如,当使用系统时钟来构造a时 隐蔽通道 在两个不应该合谋的进程之间 - 在两个不同的源中加载的javascript可以通过使用间隔来检查时间和循环以消耗处理器或不进行通信来建立低带宽信道。
这是一个很好的问题,是的,您应该考虑采取措施确保为无效用户名返回“登录被拒绝”响应所花费的时间与密码错误的有效用户名相比。
在已知用户名以便通过分析服务器进行字符串比较所花费的时间来发现哈希值以及最终密码本身的情况下,已经使用了定时攻击,因此使用了'slow_equals '比较散列的功能,以及在发现不匹配时不会提前退出的功能。
登录失败后,您永远不应该报告是否是不正确的用户名或密码,因为这样做会帮助攻击者发现有效的用户名(尽管您应该告诉用户为什么不告诉他们这是否是用户名)或密码他们错了,因为它往往激怒他们否则)。
随机攻击者需要发现有效的用户名,然后发现它们的密码。如果你帮助他们发现有效的用户名,他们就在那里。因此,如果他们可以根据您的登录脚本返回失败所花费的时间发现有效的用户名,那么您将帮助他们。
让我们考虑一下这个过程:
1.数据库查询以检索哈希和salt;
2.用盐哈希密码;
3.将检索到的哈希值与提供的密码生成的哈希值进
查询在找到匹配记录时是否提前退出?如果我们为username列指定了UNIQUE KEY,它可能会这样做,所以我们可能不应该这样做,而是在我们修改数据库时使用其他机制来避免重复。
如果数据库查询没有找到记录,我们是否会执行这些后续步骤? (你的哈希过程需要花费一点时间吗?)因此,如果用户名不匹配,则应使用'默认'哈希和盐,以便在恒定时间内完成该过程[这就是为什么Q]中的异常处理程序代码。
在这里使用'slow_equals'。
这里的回答者提出网络延迟差异使得这不是问题。我认为攻击者不需要100%的成功率,而统计分析将会显露出来。你不能太小心。
可能不是一个明确的答案,但似乎您可能能够确保所有用户名检查操作花费相同的时间。让它们在完成后进入等待状态,直到经过预定的时间。