Preventing Username Enumeration Attacks with Spring Security – 用Spring Security防止用户名枚举攻击

最后修改: 2019年 2月 17日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll describe enumeration attacks in general. More specifically, we’ll explore username enumeration attacks against a web application. And, most importantly, we’ll explore options for handling them through Spring Security.

在本教程中,我们将描述一般的枚举攻击。更具体地说,我们将探讨针对Web应用程序的用户名枚举攻击。而且,最重要的是,我们将探讨通过Spring Security处理这些攻击的方案。

2. Explaining Enumeration Attacks

2.解释列举式攻击

Enumeration technically means complete and ordered listing of all the items in a collection. Although this definition is restricted to mathematics, its essence makes it a potent hacking tool. Enumeration often exposes attack vectors that can be employed for exploitation. In this context, it is often known as resource enumeration.

枚举在技术上意味着完整和有序地列出一个集合中的所有项目。虽然这个定义仅限于数学,但其本质使其成为一个强大的黑客工具。枚举通常暴露了可用于开发的攻击载体。在这种情况下,它通常被称为资源枚举。

Resource enumeration, as the name suggests, is a way to gather a list of resources from any host. These resources can be anything of value, including usernames, services, or pages. These resources can expose potential vulnerabilities in the host.

资源枚举,顾名思义,是一种从任何主机收集资源列表的方法。这些资源可以是任何有价值的东西,包括用户名、服务或网页。这些资源可以暴露出主机的潜在漏洞。

Now, there can be several possible ways, explored or even unexplored, to exploit these vulnerabilities.

现在,可以有几种可能的方式,已经探索过的,甚至没有探索过的,来利用这些漏洞。

3. Popular Enumeration Attacks for Web Applications

3.流行的网络应用程序枚举攻击

In a web application, one of the most often employed enumeration attacks is username enumeration attack. This basically employs any explicit or implicit feature of the web application to gather valid usernames. An attacker may use popular choices of username to attack the web application.

在网络应用程序中,最常使用的枚举攻击之一是用户名枚举攻击。这基本上是利用网络应用程序的任何显性或隐性功能来收集有效的用户名。攻击者可以利用流行的用户名选择来攻击网络应用。

Now, what kind of feature in a web application may reveal whether a username is valid or not? Honestly, it can be as varied as possible. It may be a feature as designed, for example, a registration page letting a user know that the username is already taken.

现在,在一个网络应用程序中,什么样的功能可以显示出一个用户名是否有效?老实说,它可以是尽可能多的。它可能是一个设计好的功能,例如,一个注册页面让用户知道这个用户名已经被占用。

Or, this may be as implicit as the fact that a login attempt with a valid username takes a much different amount of time compared to one with an invalid username.

或者,这可能是一个隐含的事实,即用一个有效的用户名尝试登录与用一个无效的用户名尝试登录所花费的时间大不相同。

4. Setup to Emulate Username Enumeration Attack

4.模拟用户名枚举攻击的设置

We’ll use a simple user web application using Spring Boot and Spring Security to demonstrate these attack vectors. This web application will have a minimal set of features to support the demonstration. A detailed discussion on how to set up such an application is covered in a previous tutorial.

我们将使用一个使用Spring Boot和Spring Security的简单用户Web应用来演示这些攻击载体。这个Web应用将有一套最小的功能来支持演示。关于如何设置此类应用程序的详细讨论已在之前的教程中介绍过

Common features on a web application often reveal information that can be used to launch enumeration attacks. Let’s go through them.

网络应用程序上的常见功能往往会透露出可用于发起枚举攻击的信息。让我们来看看它们。

4.1. User Registration

4.1.用户注册

User registration needs a unique username, and email address is often chosen for simplicity. Now, if we pick an email which already exists, the application ought to tell us so:

用户注册需要一个独特的用户名,为了简单起见,通常选择电子邮件地址。现在,如果我们选择一个已经存在的电子邮件,应用程序应该告诉我们。

Registration

Coupled with the fact that a list of emails is not hard to come by, this can lead to a username enumeration attack to fish out valid usernames in the application.

再加上电子邮件列表并不难得,这可能会导致用户名枚举攻击,在应用程序中捞出有效的用户名。

4.2. User Login

4.2 用户登录

Similarly, when we try to login into an application, it requires us to provide username and password. Now, if a username we provide does not exist, the application may return this information to us:

同样地,当我们试图登录一个应用程序时,它要求我们提供用户名和密码。现在,如果我们提供的用户名不存在,应用程序可能会向我们返回这一信息。

Login

This, as before, is simple enough to harness for a username enumeration attack.

和以前一样,这很简单,可以用来进行用户名枚举攻击。

4.3. Reset Password

4.3.重置密码

Reset password is often implemented to send a password reset link to a user’s email. Now, again this will require that we provide a username or email:

重置密码通常是为了向用户的电子邮件发送一个密码重置链接。现在,这又需要我们提供一个用户名或电子邮件。

PasswordReset

If this username or email does not exist in the application, the application will inform as such, leading to a similar vulnerability as we saw earlier.

如果这个用户名或电子邮件在应用程序中不存在,应用程序就会如实告知,从而导致与我们之前看到的类似的漏洞。

5. Preventing Username Enumeration Attacks

5.防范用户名枚举攻击

There can be several ways to prevent a username enumeration attack. Many of them we can achieve through simple tweaks in the features like user messages on a web application.

可以有几种方法来防止用户名枚举攻击。其中许多我们可以通过简单的调整功能来实现,例如网络应用程序上的用户信息。

Moreover, Spring Security over time has matured enough to support handling many of these attack vectors. There are features out-of-the-box and extension points to create custom safeguards. We’ll explore some of these techniques.

此外,随着时间的推移,Spring Security已经足够成熟,支持处理许多这样的攻击载体。有一些开箱即用的功能和扩展点来创建自定义的防护措施。我们将探索其中的一些技术。

Let’s go through popular options available to prevent such attacks. Please note that not all of these solutions are suitable or even possible in every part of the web application. We’ll discuss this in more detail as we go along.

让我们来看看可用来防止此类攻击的流行方案。请注意,并不是所有这些解决方案都适合,甚至在网络应用的每个部分都有可能。我们将在接下来的讨论中更详细地讨论这个问题。

5.1. Tweaking Messages

5.1.调整信息

First, we must rule out all possibilities of inadvertently giving out more information than what is required. This would be difficult in registration but fairly simple in login and reset password pages.

首先,我们必须排除所有无意中提供超过要求的信息的可能性。这在注册中很难做到,但在登录和重设密码的页面中则相当简单。

For instance, we can easily make the message for login page abstract:

例如,我们可以很容易地使登录页面的信息抽象化。

LoginCorrected

We can do similar tweaks to the message for the password reset page.

我们可以对密码重置页面的信息做类似的调整。

5.2. Including CAPTCHA

5.2.包括CAPTCHA

While tweaking the messages works well on some pages, there are pages like registration where it’s tricky to do so. In such cases, we can use another tool called CAPTCHA.

虽然调整信息在一些页面上效果很好,但有些页面,如注册,这样做很棘手。在这种情况下,我们可以使用另一个叫做CAPTCHA的工具。

Now, at this point, it’s worthwhile to note that any enumeration attack most likely is robotic due to a vast number of possibilities to go through. Hence, detecting a human or robotic presence can help us prevent an attack. CAPTCHA serves as a popular way to achieve this.

现在,在这一点上,值得注意的是,由于有大量的可能性可以通过,任何枚举攻击很可能是机器人的。因此,检测人类或机器人的存在可以帮助我们防止攻击。CAPTCHA是实现这一目标的一种流行方式。

There are several possible ways to implement or integrate CAPTCHA services in a web application. One of these services is reCAPTCHA by Google, which can be easily integrated on the registration page.

有几种可能的方法可以在网络应用程序中实施或集成验证码服务。其中一个服务是Google的reCAPTCHA,它可以很容易地集成在注册页面上

5.3. Rate Limiting

5.3.速率限制

While CAPTCHA serves the purpose well, it does add latency and, more importantly, inconveniences to legitimate users. This is more relevant for frequently used pages like login.

虽然验证码的作用很好,但它确实增加了延迟,更重要的是,给合法用户带来了不便。这对于经常使用的页面,如登录,更有意义。

One technique that can help prevent robotic attacks on frequently used pages like login is rate limiting. Rate limiting refers to preventing successive attempts for a resource after a certain threshold.

一种有助于防止机器人对登录等常用页面进行攻击的技术是速率限制。速率限制指的是防止在某一阈值之后对某一资源进行连续的尝试。

For example, we can block requests from a particular IP for a day after three failed attempts at login:

例如,在三次登录失败后,我们可以阻止来自某个特定IP的请求,为期一天。

LoginBlocked

Spring Security makes this particularly convenient.

Spring Security使这一点特别方便。

We begin by defining listeners for AuthenticationFailureBadCredentialsEvent and AuthenticationSuccessEvent. These listeners call a service that records the number of failed attempts from a particular IP. Once a set threshold is breached, subsequent requests are blocked in the UserDetailsService.

我们首先为AuthenticationFailureBadCredentialsEvent和AuthenticationSuccessEvent定义监听器。这些监听器调用一个服务,记录来自特定IP的失败尝试次数。一旦突破了设定的阈值,后续的请求就会在UserDetailsService中被阻止。

A detailed discussion on this approach is available in another tutorial.

关于这种方法的详细讨论可参见另一篇教程

5.4. Geo Limiting

5.4 地理限制

Additionally, we can capture the location by country of a user during registration. We can use this to verify a login attempt originating from a different location. If we detect an unusual location, suitable action can be taken:

此外,我们可以在用户注册时按国家捕获其位置。我们可以用它来验证一个来自不同地点的登录尝试。如果我们检测到一个不寻常的位置,可以采取适当的行动。

  • Enable Captcha selectively
  • Enforce step-up authentication (as part of multi-factor authentication)
  • Ask the user to verify the location securely
  • Block the user temporarily on successive requests

Again, Spring Security, through its extension points, makes it possible to plug in a custom location verification service in the AuthenticationProvider. A particular flavor of this has been described in detail in a previous tutorial.

同样,Spring Security通过其扩展点,可以在AuthenticationProvider中插入自定义位置验证服务。在之前的教程中已经详细介绍了这种特殊的风味

5.5. Multi-Factor Authentication

5.5.多因素认证

Lastly, we should note that password-based authentication is often the first and, in most cases, the only step required. But it’s not uncommon for applications to adopt multi-factor authentication mechanisms for better security. This is especially true for sensitive applications like online banking.

最后,我们应该注意到,基于密码的认证往往是第一步,而且在大多数情况下,是唯一需要的步骤。但是,应用程序采用多因素认证机制以提高安全性的情况并不少见。这对于像网上银行这样的敏感应用来说尤其如此。

There are many possible factors when it comes to multi-factor authentication:

谈到多因素认证,有许多可能的因素。

  • Knowledge Factor: This refers to what a user knows, like PIN
  • Possession Factor: This refers to what a user possesses, like a token or smartphone
  • Inherence Factor: This refers to what a user inherently has, like fingerprints

Spring Security is quite a convenience here as well, as it allows us to plug in a custom AuthenticationProvider. The Google Authenticator app is a popular choice to implement additional possession factor. This allows users to generate an ephemeral token on the app in their smartphone and use it for authentication in any application. Obviously, this requires setting up the user beforehand in the application, either during registration or later on.

Spring Security在这里也很方便,因为它允许我们插入一个自定义的AuthenticationProvider。Google Authenticator应用程序是实现额外占有因素的一个流行选择。这允许用户在其智能手机的应用程序上生成一个短暂的令牌,并在任何应用程序中使用它进行认证。显然,这需要事先在应用程序中对用户进行设置,可以在注册时或稍后进行。

Integrating Google Authenticator in a Spring security application has been well covered in a previous tutorial.

在Spring安全应用程序中集成Google Authenticator已在之前的教程中详细介绍

More importantly, a solution like multi-factor authentication is only suitable if the application needs it. Hence, we should not use it primarily to prevent enumeration attacks.

更重要的是,像多因素认证这样的解决方案只有在应用需要时才适用。因此,我们不应该主要使用它来防止枚举攻击。

5.6. Processing Time Delays

5.6.处理时间的延迟

While processing a request like a login, checking if the username exists is often the very first thing we do. If a username does not exist, the request immediately returns with an error. On the contrary, a request with a valid username would involve many further steps, like password match and role verification. Naturally, the time to respond to both these cases may vary.

在处理像登录这样的请求时,检查用户名是否存在往往是我们做的第一件事。如果一个用户名不存在,请求会立即返回错误。相反,一个有有效用户名的请求会涉及许多进一步的步骤,比如密码匹配和角色验证。自然,对这两种情况的响应时间可能会有所不同。

Now, even though we abstract the error message to hide the fact of whether a username is valid or not, a significant difference in processing time may tip off an attacker.

现在,尽管我们对错误信息进行了抽象化处理,以掩盖用户名是否有效的事实,但处理时间上的显著差异可能会让攻击者察觉。

A possible solution for this issue can be to add a forced delay to rule out the difference in processing times. However, as this is not a problem that can occur with certainty, we should only employ this solution if necessary.

这个问题的一个可能的解决方案可以是增加一个强制延迟来排除处理时间的差异。然而,由于这不是一个可以确定发生的问题,我们应该只在必要时采用这种解决方案。

6. Wrapping Up

6.收尾工作

While we covered a lot of tricks to use when it comes to username enumeration attacks, it’s natural to ask, when to use what? Obviously, there’s no one answer for this, as it’s largely based on the type of application and its requirements.

虽然我们在涉及到用户名枚举攻击时涵盖了很多技巧,但很自然地会问,什么时候使用什么?显然,这个问题没有唯一的答案,因为它主要是基于应用程序的类型和它的要求。

A few things, like messages to the user, must leak as little information as possible. Additionally, it’s wise to restrict successive failed attempts towards a resource like login.

有几件事,如给用户的信息,必须尽可能少地泄露信息。此外,明智的做法是限制对一个资源的连续失败尝试,如登录。

However, we should use any additional measures only if requirements deem them necessary. We should also weigh them rationally against the deterrence to usability.

然而,只有在要求认为有必要时,我们才应该使用任何额外的措施。我们还应该合理地权衡它们与对可用性的阻碍。

Moreover, it’s important to realize that we can apply any combination of these measures for different resources to selectively secure them.

此外,重要的是要认识到,我们可以对不同的资源应用这些措施的任何组合,以有选择地保护它们。

7. Conclusion

7.结论

In this tutorial, we discussed enumeration attacks – username enumeration attacks, in particular. We saw that through the lens of a simple Spring Boot application with Spring Security.

在本教程中,我们讨论了枚举攻击–特别是用户名枚举攻击。我们通过一个带有Spring Security的简单Spring Boot应用程序的视角看到了这一点。

We went over several ways to progressively address the concerns of username enumeration attacks.

我们研究了几种方法来逐步解决用户名枚举攻击的问题。

Lastly, we discussed the appropriateness of these measures in application security.

最后,我们讨论了这些措施在应用安全方面的适当性。

As always, the code for the examples is available over on GitHub.

像往常一样,这些例子的代码可以在GitHub上找到over