Skip to content

Hashing and Salting passwords with Spring Security PasswordEncoder

A standard Spring Security configuration uses username / password based authentication. This always presents a tricky problem: how to securely store a user’s password in such a way that it can’t be read by anyone with access to our database. It’s naive to assume that our password database is 100% secure, just ask Adobe, Sony, Ashley Madison, and every other large organization that has had their database breached. Even if the database isn’t ‘breached’ or ‘leaked’, legitimate database admins or sys admins still have access to user passwords. A database containing user passwords is a liability that we’d rather not have.

The standard solution to this problem is store store a hash of the password rather than the plain text or even encrypted text. I don’t want to focus on why this is good or how it works as many others have done this already. I’ve found no better discussion of this (and password management in general) than Troy Hunt’s post on Everything you ever wanted to know about building a secure password reset feature.

Getting the details right when implementing password storage is critical. Some hash algorithms are vulnerable or just not suited to password hashing. If the salt is too short or predictable, it may be possible to retrieve the password from the hash. Any number of subtle bugs in coding could result in a password database that is vulnerable in one way or another. Fortunately, Spring Security includes password hashing out of the box. What’s more, since version 3.1, Spring Security automatically takes care of salting too.

The following example is available to download from GitHub in version 3.4 of the Spanners app.

Spring Security: database backed user authentication

For what it’s worth, I still prefer the old-skool XML namespace configuration over the newfangled Java configuration for Spring Security. I find it to be more readable. The following examples all use the Spring Security XML namespace but would work equally well using Java configuration.

Spring Security offers database backed user authentication allowing a username and password to be verified against a database table:

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService"/>
</authentication-manager>

<beans:bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
	<beans:property name="dataSource" ref="spannersDS"/>
</beans:bean>

This will verify a username / password against the users table of the database referred to via the dataSource property. The database is set up with the default schema as detailed in the JdbcDaoImpl documentation.

Configuring a Password Encoder

The BCrypt password encoder can be configured simply by having the authentication-provider refer to it:

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService">
		<password-encoder hash="bcrypt"/>
	</authentication-provider>
</authentication-manager>

In previous versions of Spring, it was necessary to specify a salt source too. As of version 3.1, all implementations of the new PasswordEncoder interface take care of salting too.

The above configuration uses the BCrypt password encoder with default settings. If you want to use a non-standard PasswordEncoder implementation, or if you need to refer to a PasswordEncoder  bean, this is also possible:

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService">
		<password-encoder ref="passwordEncoder"/>
	</authentication-provider>
</authentication-manager>

<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

User admin

The above configuration of the authentication manager, authentication provider and password encoder deal only with verifying a password on login. We also need some means of creating user accounts with hashed passwords. An obvious way to do this is with a self-service registration page that asks new users to choose a username and password:

sign up

The implementation behind this will create a new row in the users table with the given username and password. The passwordEncoder bean can be reused to create the hash of the password entered by the user. An instance of Spring’s JdbcUserDetailsManager can be used to create the account. JdbcUserDetailsManager is a subclass of JdbcDaoImpl (and obviously implements the UserDetailsService interface) so the same bean can be used by the authentication manager to lookup user details on authentication.

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsManager">
		<password-encoder ref="passwordEncoder"/>
	</authentication-provider>
</authentication-manager>

<!-- UserDetailsManager acts as a UserDetailsService for the authentication provider
	 and also allows CRUD operations on users and groups. Backed by JDBC - the spanners database. -->
<beans:bean id="userDetailsManager" class="org.springframework.security.provisioning.JdbcUserDetailsManager">
	<beans:property name="dataSource" ref="spannersDS"/>
</beans:bean>

<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

The userDetailsManager and passwordEncoder beans used for authentication are injected (autowired) into the Spring MVC controller for the signup page, allowing account creation:

@Controller
public class SignupController {

    @Autowired private UserDetailsManager userDetailsManager;
    @Autowired private PasswordEncoder passwordEncoder;
    
    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public String signup(@Valid @ModelAttribute SignupForm signupForm, Errors errors) {
        if (errors.hasErrors()) {
            return null;
        }

        // Password should be stored hashed, not in plaintext
        String hashedPassword = passwordEncoder.encode(signupForm.getPassword());

        // Roles for new user
        Collection<? extends GrantedAuthority> roles = Arrays.asList(
                new SimpleGrantedAuthority("ROLE_VIEWER"),
                new SimpleGrantedAuthority("ROLE_EDITOR")
        );

        // Create the account
        UserDetails userDetails = new User(signupForm.getName(), hashedPassword, roles);
        userDetailsManager.createUser(userDetails);

        return "redirect:/";
    }
}

Which PasswordEncoder implementation / hashing algorithm to use?

Spring Security offers two implementations of the new PasswordEncoder interface – BCryptPasswordEncoder and the confusingly named StandardPasswordEncoder based on SHA-256. The BCrypt implementation is the recommended one. There’s also a NoOpPasswordEncoder which does no encoding. It’s intended for unit testing only.

Spring Security also has implementations based on MD4, MD5 and SHA-1 which implement a previous PasswordEncoder interface. This version of the interface is now marked as deprecated as the new one deals better with salting. Confusingly, the implementations are not specifically marked as deprecated. They should however be avoided as they are based on hashing algorithms now known to be insecure. Their only legitimate use is backwards compatibility with legacy applications.

BCrypt is widely accepted as a suitably secure password hashing function. Both OWASP and an excellent explainer on CrackStation recommend BCrypt. PBKDF2 and scrypt are also frequently mentioned as suitable candidates for password encoding but no standard implementation exists for either of these in Spring Security. While it is possible to create a custom implementation of PasswordEncoder using any algorithm, I’d usually recommend sticking to a provided implementation if at all possible.

Published inHow ToSecurity

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *