Spring Security: how to implement Brute Force Detection (BFD)?

Kdeveloper picture Kdeveloper · Apr 21, 2010 · Viewed 13.5k times · Source

My web applications security is handled by Spring Security 3.02 but I can't find any out of the box support for Brute Force Detection.

I would like to implement some application level BFD protection. For example by storing failed login attempt per user in the database (JPA). The attacked user accounts could then get a lockout period or a forced account re-activation by e-mail.

What's the best way to implement this with Spring Security? Does any body have example code or best practices on this?

Answer

Kdeveloper picture Kdeveloper · Apr 21, 2010

It's not that hard to roll your own BFD. As in Spring Security 3.0 you can simply add Application listeners (thanks Stephen C for pointing me in the correct direction).

This listener will be called when authentication failures appear:

@Component
public class AuthenticationFailureListener
    implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {

  @Autowired
  private UserDao userDao;

  public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent ev) {

    String username = ev.getAuthentication().getName();

    User user = userDao.find("name", username);
    if (user != null) { // only for existing users
            user.reportLoginFailure();
            userDao.commit();
    }
  }
}

Each authentication failure will now inform the user. The user for example increments an authentication failure counter and deactivates it self when a certain threshold is reached.

When a user is correctly authenticated the below listener will inform the user (who for example can reset it’s authentication failure counters):

@Component
public class AuthenticationSuccessEventListener
    implements ApplicationListener<AuthenticationSuccessEvent>{

  @Autowired
  private UserDao userDao;

  public void onApplicationEvent(AuthenticationSuccessEvent event) {

    String username = event.getAuthentication().getName();

    User user = userDao.find("name", username);
    user.reportLoginOK();
    userDao.commit();
  }
}

The above listeners will not need additional XML configuration and are picked up automatically by Spring (if they are in Spring component-scan package).

Depending on you transaction configuration this solution could miss some failed login counts if they happen near simultaneously. This can be prevented if you update the counter with a single UPDATE query instead of loading the user and then save the changes.

Above listeners can also be extended to detect other BDF patterns, for example a single IP that is doing a scan on lot’s of (random) user names.