Password Encoding with Spring Security

12 min read ⋅ 2999 views

Password Encoding is one of the most basic, but also one of the most important security measures to take when building a web application. 

This blog is targeted towards new and intermediate developers who have some experience with Spring who haven't had experience with password encoding via Spring Security and wish to expand their knowledge.

Need for Password Encoding

Nowadays, we have to expect potential attacks on our application as well as take some safety precautions to make sure that nobody obtains someone else's authentication or authorization.

Unfortunately, it's not that hard to gain at least viewing access to a database, either through SQL Injection, SQL Obfuscation or by other means. That means that it's easy for an attacker to obtain all credentials for all users in your application.

Fortunately, if done right, encoding them can stop an attacker right there and then easily.

How Not To Store Passwords?

 Literal Values

In most tutorials online, we simply store passwords as they are - in literal plain text format.
When logging in, the application compares the input password to the password stored in the database - if it matches, you gain access.

This is very bad because anybody with view rights in the database can simply copy and paste the correct passwords.

Encryption

Encryption is simple, we input a password that is then considered a source. Using the source, a hidden key is generated. Based on the key, your password is converted into a random String of characters and symbols.
When logging in, the application uses this hidden key is used to convert the input password. Then the application compares these converted passwords - if they match, you gain access.

This is slightly better, but still not secure. It just slows down the attacker, because it's easy to decrypt a password if they obtain the key - which is frequently stored on the same server.

Hashing Passwords

Hashing is similar to encryption, but it offers a one-way ticket. There is no key to decrypt the password - you can't use the same tool to get the original password like you can with encryption.
When logging in, the application hashes the input password and compares the hash with the stored hashed password - if they match, you gain access.
Popular hashing functions include MD5, SHA-1 and SHA-256.

This is better than encryption since it's harder to convert back to the original password. An attacker would have to try all different combinations and hash them to compare them to the password in the database. This isn't the safest option since there are numerous rainbow tables available online. Rainbow tables are lists of leaked and commonly used passwords and their hashes. This means that complex and unique passwords are safe, but simple and common ones like "password" are easy to crack.

Salting Passwords

Salting is basically an updated hashing method. It works by adding a "salt" to a beginning of a password - a random sequence of characters. 

This is better than hashing since even simple and known passwords like "password" can't be found in public rainbow tables.

How to Store Passwords?

Since we've gone over 4 methods that offer security, but still not enough to have a safe application.

Slow Hashes

Slow hashing is the way to go. While it's not perfect, like all other methods, it's currently the best and offers a safety standard. The biggest threat to hashed passwords is their speed. They work very fast, which allows attackers to try all possible combinations very fast.
On the other hand, using slower hashes will dramatically slow down an attacker to the point where they might simply give up due to time consumption.

Slow hashing is currently considered to be the best approach when storing passwords so it's also considered standard practice to implement it. 
BCrypt is a popular algorithm that does just that - it both salts passwords and slows them down after the iteration count increases enough for it to be considered an attack.

Encoding Passwords with Spring Security

With all of the above in mind, we can make a simple Spring MVC application that uses Spring Security and BCrypt to safely store our password in a database.

Let's firstly show BCrypt in action, with a simple method that encodes the same String "password123" ten times:

    public void encode(String password) {

for(int i = 0; i < 10; i++) {

BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

String encodedPassword = passwordEncoder.encode(password);

System.out.println(encodedPassword);

}

}

Running this will print out our encoded values:

$2a$10$rgCdXX5P2V4RCuAOttEhnO4ZWdaqqrgn4OfPRDxrSk6p4YCEzt.7q

$2a$10$lGjFjy5wI2IDWh7niUrWQeyf6djivwytzXG.0Id98fZEOON2teVeO

$2a$10$OS6cBoVPUPo2Cmno7lDq3.WZpHkICVgjv1c7HGFx.I6luDMI1vABS

$2a$10$zNbJnMxmjwmUk6DVg.3RTuEarbf9rPcM0SxZacukDTAKC4wbBnzXy

$2a$10$ya3zeh0puo9b7A0SORwYGuCGnhgFjLSffQ3qN9MMQ/1EMrU47p4Ze

$2a$10$/8fVjKwH.PNTMHi9m4g2FeQLWyjs99RaYSspSRX1ioIZ6HhMrpyku

$2a$10$Eg9iEa0KoEX5IHqd8aTsju2fpE2xJKT.iP7i1JJjFG5GnsFvVJ1rK

$2a$10$cAszpuQuSVvhgVkr1csDq.ytCpkoYKPft4l8gVSrr1nWQR3DRyQK2

$2a$10$g4a/o/wPCnkX7rEAIZiVNOfZvKqF5sD2zN2wEG1.ovLaJQDBIaa4u

$2a$10$ZB5usG9cVWAoMnebSDZZj.pqOVN6JLDTjkZQQgFSrD5RAlQfwaLYS

We can clearly see different values each time it's encoded. However, we can see a repeating pattern at the beginning - $2a$10$.
This prefix holds some information about the nature of the hashing algorithm.

$2a$ Indicates that the hash String is a BCrypt hash in modular crypt format.
10$
This number indicates the cost parameter - 2^10 expansion rounds.
rgCdXX5P2V4RCuAOttEhnO
Salt in our password.
4ZWdaqqrgn4OfPRDxrSk6p4YCEzt.7q
Resulting hash of our password.


There's a long way from "password123" to "rgCdXX5P2V4RCuAOttEhnO4ZWdaqqrgn4OfPRDxrSk6p4YCEzt.7q".

If BCrypt hashes the same password differently each time, how does it compare and match passwords? BCrypt compares the input password from the POST request with the hashed password in the database by breaking it down to the salt, the cost parameter, and the hashed value. Since the salt influences the generated hash, this is enough information to determine whether they match or not. 

Creating our own application

Now that we are familiar with password encoding, we can go ahead and make an application, from scratch, that allows users to register, encodes their password using BCrypt and then allows them to log in securely.

File hierarchy


Dependencies

<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-orm</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-core</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.hibernate.javax.persistence</groupId>

<artifactId>hibernate-jpa-2.0-api</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>jstl</groupId>

<artifactId>jstl</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>taglibs</groupId>

<artifactId>standard</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>servlet-api</artifactId>

<version>${version}</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-validator</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>javax.validation</groupId>

<artifactId>validation-api</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework.security</groupId>

<artifactId>spring-security-core</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework.security</groupId>

<artifactId>spring-security-web</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>org.springframework.security</groupId>

<artifactId>spring-security-config</artifactId>

<version>${version}</version>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>${version}</version>

</dependency>

</dependencies>


Setting up the application

I personally like to use XML Configuration files, over Java-based configuration, since to me it looks cleaner and more organized.

web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1">

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<!--Optional listener for a debug logger-->

<listener>

<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>

/WEB-INF/applicationContext.xml,

/WEB-INF/dispatcher-servlet.xml

</param-value>

<!--Optional configuration for a debug logger-->

<param-name>log4jConfigLocation</param-name>

<param-value>

/WEB-INF/log4j.properties

</param-value>


</context-param>

<servlet>

<servlet-name>dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>dispatcher</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

</web-app>

dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<context:component-scan base-package="com.ablebio"/>

<mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"/>

<tx:annotation-driven/>

<mvc:annotation-driven/>


<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/views/"/>

<property name="suffix" value=".jsp"/>

</bean>

</beans>

applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:security="http://www.springframework.org/schema/security"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security.xsd ">

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://localhost:3306/ablebio"/>

<property name="username" value="root"/>

<property name="password" value=""/>

</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

<property name="dataSource" ref="dataSource"/>

<property name="hibernateProperties">

<props>

<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>

<prop key="hibernate.hbm2ddl.auto">update</prop>

<prop key="hibernate.show_sql">true</prop>

<prop key="hibernate.format_sql">true</prop>

</props>

</property>

<property name="packagesToScan">

<list>

<value>com.ablebio</value>

</list>

</property>

</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">

<property name="sessionFactory" ref="sessionFactory"/>

</bean>

<security:http auto-config="true">

<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN"/>

<security:intercept-url pattern="/users/**" access="ROLE_USER"/>

<security:form-login

login-page="/login"

default-target-url="/"

authentication-failure-url="/login?error"

username-parameter="username"

password-parameter="password"

/>

<security:logout logout-success-url="/login?logout"/>

</security:http>

<security:authentication-manager>

<security:authentication-provider user-service-ref="userDetailsManager">

<security:password-encoder ref="passwordEncoder"/>

</security:authentication-provider>

</security:authentication-manager>

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

<constructor-arg name="strength" value="11"/>

</bean>

<bean id="userDetailsManager" class="org.springframework.security.provisioning.JdbcUserDetailsManager">

<property name="dataSource" ref="dataSource"/>

</bean>

</beans>

I'm using MySQL as my preferred database management system, but switching it out with any other should work fine as well.

Later on, we use Spring Security to configure which URL patterns should be intercepted and asked for authentication/authorization upon request to access. This won't mean much for our application though since we won't have any pages specified under /admin or /users, just public pages for registration and login.

After that, I defined a login form for Spring to look out for:

login-pageThe location of the login page
default-target-urlThe URL to be redirected to upon successful login
authentication-failure-urlThe URL to be redirected to upon unsuccessful login
username-parameterParameter that will be used as a username in SQL queries to authenticate the user
password-parameter
logout-success-url
Parameter that will be used as a password in SQL queries to authenticate the user
The URL to be redirected to upon successful logout


Next up, we need to define our authentication manager. In the authentication manager, we set our userDetailsManager bean as the authentication provider and our passwordEncoder bean as the password encoder. I decided to use JdbcUserDetailsManager, which is a built-in user details manager for simplicity of this tutorial. 

You can use your own custom details manager, or define the queries for this one yourself:

<security:jdbc-user-service

data-source-ref="dataSource"

authorities-by-username-query="SELECT username, authority FROM Authorities WHERE username = ?"

users-by-username-query="SELECT username, password, enabled FROM Users WHERE username=?"/>

This is also where we define our BCrypt bean, in which we simply defined a strength constructor argument.

With all of this set up, let's jump into our domain model, controllers and DAOs.

Domain Model

We require just two simple models for our application:

 Users:

@Entity

public class Users {

@Id

@GeneratedValue

private int userId;

private String username;

private String password;

private boolean enabled;


//getters and setters

}

In it, we have a few simple fields like the userId, username, password and a boolean if the user is enabled. All of these, except the userId are required for authentication with Spring Security.

Authorities:

@Entity

public class Authorities {

@Id

@GeneratedValue

private int authoritiesId;

private String username;

private String authority;


// getters and setters

}

We also require separate Authorities for Spring's user details management to work.

DAO

We have no need for a custom DAO to add or retrieve users from the database since JdbcUserDetailsService will be doing that for us. However, I still made a simple DAO to fetch users etc. in order to check whether a username already exists upon registration.

UsersDAO:

@Repository

@Transactional

public class UserDAO {


@Autowired

SessionFactory sessionFactory;


public Users getUserByUsername(String username) {

Session session = sessionFactory.getCurrentSession();

Query query = session.createQuery("from Users where username = ?");

query.setString(0, username);

return (Users) query.uniqueResult();

}


public List<Users> getAllUsers() {

Session session = sessionFactory.getCurrentSession();

Query query = session.createQuery("from Users");

List<Users> usersList = query.list();

return usersList;

}

}


Controller

@Controller

public class MainController {


@Autowired

private UserDetailsManager userDetailsManager;


@Autowired

private PasswordEncoder passwordEncoder;


@RequestMapping("/")

public String index() {

return "index";

}


@RequestMapping("/login")

public String login(

@RequestParam(value = "error", required = false) String error,

@RequestParam(value = "logout", required = false) String logout, Model model) {

if(error != null) {

model.addAttribute("error", "Wrong username or password!");

}

if(logout != null) {

model.addAttribute("msg", "You have successfully logged out.");

}

return "login";

}


@RequestMapping("/register")

public String test(Model model) {

Users users = new Users();

model.addAttribute("user", users);

return "register";

}

@RequestMapping(value = "register", method = RequestMethod.POST)

public String testPost(@Valid @ModelAttribute("user") Users user, BindingResult result, Model model) {

if (result.hasErrors()) {

return "register";

}

<!-- check if username already exists -->

String hashedPassword = passwordEncoder.encode(user.getPassword());

Collection<? extends GrantedAuthority> roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));

UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), hashedPassword, roles);

userDetailsManager.createUser(userDetails);

return "registerSuccess";

}

}

First, we autowire our user details manager and our password encoder beans.

Since in our applicationContext.xml, we defined our failure URL to be "login?error", we check for the error parameter - if it exists, we assign the error message to "Wrong username or password!".
Additionally, since we defined our logout success URL to be "/login?logout", we check for the logout parameter - if it exists, we assign the message to "You have successfully logged out."

Then we have our register method that accepts a submission to register in which we bind our user from the form to our Users model to populate our fields.

First off, we encode the password of the user with our password encoder. This is done in a similar fashion to the first example of hashing a password.
Then we assign a simple role "ROLE_USER" to our new user.
To tie our username, hashed password and roles into one object - we create userDetails.

Note: To avoid confusion between Users and User, I included the whole user details User declaration and skipped the import.

In the end, we create the user based on the details and return a success page to the user.

JSP Pages

To finally complete the application, we need some pages.

 index.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>

<head>

<title>Home</title>

</head>

<body>

<c:if test="${pageContext.request.userPrincipal.name == null}">

<h1>Please <a href="/login">login</a> or <a href="/register">register</a>.</h1>

</c:if>

<c:if test="${pageContext.request.userPrincipal.name != null}">

<h1>Welcome ${pageContext.request.userPrincipal.name}! | <a href="<c:url value="/j_spring_security_logout"/>">Logout</a></h1>

</c:if>

</body>

</html>

This page checks whether we are logged in or no, by checking if the name of the user retrieved by Principal is null. If it is, we are asked to register or to login. Otherwise, we are welcomed to the website, and offered to log out using /j_spring_security_logout

 register.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>

<head>

<title>Title</title>

</head>


<body>

<h2>Please fill in your credentials to register:</h2>

<form:form action="${pageContext.request.contextPath}/register" method="post" modelAttribute="user">

<h4>Username</h4>

<label for="username">Username: </label>

<form:input path="username" id="username"/>

<h4>Password</h4>

<label for="password">Password: </label>

<form:password path="password" id="password"/>

<input type="submit" value="Register">

</form:form>

</body>

</html>

This page has a Spring form which references our post register method, with the model attribute "user".

Note: Instead of modelAttribute it's legitimate to use commandName as they point to the same field. Some consider using commandName the "old way" thus branding modelAttribute better practice.

login.jsp

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>

<head>

<title>Login</title>

</head>

<body>

<div id="login-box">

<h2>Log in using your credentials!</h2>

<c:if test="${not empty msg}">

<div class="msg">

${msg}

</div>

</c:if>

<form name="loginForm" action="<c:url value="/j_spring_security_check"/>" method="post">

<c:if test="${not empty error}">

<div class="error" style="color:red">${error}</div>

</c:if>

<div class="form-group">

<label for="username">Username: </label>

<input type="text" id="username" name="username" class="form-control"/>

</div>

<div class="form-group">

<label for="password">Password: </label>

<input type="password" id="password" name="password" class="form-control"/>

</div>

<input type="submit" value="Login" class="btn btn-default"/>

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

</form>

</div>

</body>

</html>

 To submit our login for Spring to review, we use j_spring_security_check.

Note: Since Spring Security 4, j_spring_security_check was replaced with login. If you're using newer versions, consider using the appropriate action.

The form inputs for username and password will be used by Spring to authenticate the user, like we defined in the login-form tag in our applicationContext.xml.
In the end, we included a hidden parameter - a CSRF token.

CSRF 

Since I think it requires a bit of explanation, here's a small section on Cross Site Request Forgery.

Cross-Site Request Forgery relies on active sessions from websites they intend to attack. For an example, you log into your Facebook account, and while you're logged in, you visit a sketchy website. This website uses your active session on Facebook to post a scam link on your wall. Fortunately, Facebook has CSRF protection, so this isn't as easy.

If you'd like to read more on CSRF and protection against it, there's official Spring documentation about it. 

Application in action

Let's run our application, register as a user, observe our database, and finally log in.

Our homepage politely asks us to either login with an existing account, or register as a new user. Since we're new here, let's register:

We set the username to "Rhett" and the password to "password123". Upon clicking "Register", we're greeted with our success page:

Now we can check our database, to see what has changed:

There are three users, and Rhett, our newest one. We can see that his password is safe and encoded in our database.

Also, Rhett has a role of a simple user.

Let's go back to our login page and enter the credentials.

Since we're logged in, our home page welcomes us back to the site.

Upon clicking "logout", we have successfully logged out.

Also, if the credentials don't match, we can't get in.

Source Files

If you'd like to download the application, play around with the files, configurations, or have encountered a problem and wish to use them as a guide, feel free to check out the repo on GitHub below:



Spring Java