Rob Winch (Migrated from SEC-2056) said:
Description:
Spring Security's DaoAuthenticationProvider authenticates users by utilizing the PasswordEncoder interface to compare the submitted password with the actual password. If a user is not found, the comparison is skipped which, depending on the PasswordEncoder implementation, can result in a significant difference in the amount of time required to attempt to authenticate an actual user versus a user that does not exist. This opens up the possibility of a side channel attack that would enable a malicious user to determine if a username is valid.
Example:
The DaoAuthenticationProvider allows setting of the PasswordEncoder for password validation:
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(new BCryptPasswordEncoder());
// Authentication or failure to authenticate an actual user takes ~84ms
Authentication existingUser = new UsernamePasswordAuthenticationToken("existingUser", "password");
provider.authentication(existingUser);
// Failure to authenticate a user that does not exist takes ~0ms
Authentication missingUser = new UsernamePasswordAuthenticationToken("missingUser", "password");
provider.authentication(missingUser);
The difference between the amount of time it takes to authenticate an existing user and a user that does not exist can reveal if a username is valid or not.
Mitigation:
Applications which use DaoAuthenticationProvider and a PasswordEncoder other than PlainTextEncoder are likely to be vulnerable.
All users may mitigate this issue by upgrading to Spring Security 3.1.3+, 3.0.8+, or 2.0.8+.
Fix:
DaoAuthenticationProvider now performs PasswordEncoder.isPasswordValid when a user is not found.
Credit:
The issue was discovered by Nicholas Goodwin.
Comment From: platinumvoid
Is it possible to achieve the same effect by just using a random time sleep (70-100) Thread.sleep(((long) (Math.random()*(30))) + 70);?
Comment From: jzheaux
The problem with adding random sleeps is that an attacker can at the same time overtax the system to create greater disparity between the two request types. Under normal circumstances, say, both the password hash and the random sleep increase the request time by n cycles. But, while the system is taxed, password hashing may take 10n cycles while the random sleep will still take n cycles.
The best solution is for each path to do the same work - if a password is provided, then hash it, even when the username is invalid.
Comment From: rwinch
Random sleep can be normalized out. As Josh mentioned constant time is the way to avoid timing attacks.