为什么Spring Security使用默认的预认证检查?

问题描述:

我最近在我的一个雇主的基于Spring的Java应用程序中实现了一些安全性改进,并且我已经覆盖了Spring Security的AbstractUserDetailsAuthenticationProvider类,以便对用户身份验证做一些额外的处理。在这个过程中,我意识到DefaultPreAuthenticationChecks内部类在通过验证密码的additionalAuthenticationChecks方法运行身份验证提供程序之前执行用户帐户检查。如果用户被禁用,过期或锁定,将抛出异常,因此相关消息将显示在屏幕上。对我而言,在成功验证密码之前检查用户帐户并提供此帐户的详细信息是公然的安全风险,因为它可能会暴露用户帐户是否存在。有没有人知道Spring Security为什么会这样做?很显然,我可以用check方法创建我自己的虚拟类来替代DefaultPreAuthenticationChecks类,但这种方法一开始就不成立,这真是一种耻辱。为什么Spring Security使用默认的预认证检查?

在此先感谢。

P.S.我在相关说明here上发现了一个问题,但似乎没有人问这个问题,为什么存在这种潜在的安全缺陷。

+0

可能是一个表现的东西。如果用户被禁用,那么不要做昂贵的bcrypt操作? –

+0

这是一个很好的观点,但我宁愿先完成检查,以确保试图访问帐户的人至少知道密码。然后,我们知道它很可能是授权账户持有人,因此我们很高兴告诉他们该账户不幸被锁定或禁用。我想这是速度超过额外安全和处理的选择。每次谈到认证时我都会选择后者! –

我想我有点迟到了,但如果有些人还是想知道,社会上其实有discussed this issue before

从开发商

卢克·泰勒说引述:

事实并非如此。这取决于您在登录失败时显示给用户 什么失败消息 - 没有什么可以阻止您说 “登录失败”,无论原因如何。 应用程序通知用户在 固定次数的登录尝试后,其帐户已被锁定也并不罕见。期望的行为将取决于你如何解释不同的状态标志(这不是框架严格定义的 )。例外情况也驱动AuthenticationManager生成事件,所以它们不一定在那里供用户使用。例如,系统管理员可能希望 在有人尝试使用锁定或禁用的帐户时被通知,例如,不仅在他们使用正确的密码时。

也可以认为,检查帐户 锁定状态之前的密码,并只显示在一个正确的 给出口令也将使强力密码检查甚至 “锁定”的消息后,该帐户已被锁定。

所以我不同意这种行为是“不正确的”。我认为它可能是 有用,可以定制的东西,虽然。也许我们应该有 方法

preAuthenticationChecks(用户的UserDetails)

postAuthentication(用户的UserDetails)

可能被覆盖时,递延标志 检查改变。

TL;博士:社会上有人注意到这个问题,但他们并不认为这是一个潜在的安全漏洞,相反,他们认为这取决于“你如何解释不同的状态标志”,是的,这是容易改变使用默认行为setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks)setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks)

它绝对似乎是性能优化。此外,在检查密码正确之前检查已禁用或锁定的帐户更安全,而不是反之。

由于尼尔提到,如果您之前做其他检查,然后昂贵的操作,如bcrypt可以避免。

因此我们来考虑第二点,即安全问题。考虑你的应用程序首先检查密码,如果密码有效,它会执行其他检查,如帐户被锁定/禁用等。 对于潜在的黑客来说,理解此行为并应用暴力破解用户密码并不需要很长的时间因为他知道只有在密码正确的情况下才会显示账户锁定/禁用信息。

现在回到您的问题,使用这种方法可能会暴露用户帐户是否存在的风险。首先,我认为破解密码的风险大于暴露该账户存在或不存在的风险。其次,应用程序可以捕获这些检查引发的异常(锁定/禁用)并提供自定义消息。例如。

catch(LockedException e){ 
    // log actual reason, so that it could be used for debugging purpose 
    return "Invalid credentials"; // or throw BadCrentials exception 
} 

更多细节,在这里:https://github.com/spring-projects/spring-security/issues/798