6.4. Password Hashing¶
Table of contents
6.4.1. Overview¶
org.springframework.security.crypto.password.PasswordEncoder
interface, as the password hashing mechanism.org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
org.springframework.security.crypto.password.StandardPasswordEncoder
PasswordEncoder
mechanism, password is hashed by encode(String rawPassword)
method and the encoded password is verified bymatches (String rawPassword, String encodedPassword)
method.6.4.2. How to use¶
List of PasswordEncoder implementation classes
PasswordEncoder implementation classes | Overview |
---|---|
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder |
Encoder that performs hashing using “bcrypt” algorithm
|
org.springframework.security.crypto.password.StandardPasswordEncoder |
Encoder that performs hashing with “SHA-256” algorithm + 1024 rounds of stretching
|
org.springframework.security.crypto.password.NoOpPasswordEncoder |
Encoder that does not perform hashing (for testing)
|
It is recommended to use BCryptPasswordEncoder
when hashing is not required.
However, the calculation time taken by BCryptPasswordEncoder
to improve counter-attacks being more,
if the performance requirements at the time of authentication are not fulfilled, StandardPasswordEncoder
should be considered.
In case of any restrictions concerning salt and hashed algorithm owing to the relation with existing system,
implementation class of org.springframework.security.authentication.encoding.PasswordEncoder
interface, which will be described later, should be used.
For details, refer How to extend.
6.4.2.1. BCryptPasswordEncoder¶
BCryptPasswordEncoder
is the class that implements PasswordEncoder
and provides password hashing.Note
In Bcrypt algorithm, number of calculations have been intentionally increased to more than the calculations of the standard algorithms. Therefore, compared to standard algorithms (SHA, MD5), it has strong features to resist “offline brute force attack”.
6.4.2.1.1. Configuration example of BCryptPasswordEncoder¶
applicationContext.xml
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> <!-- (1) -->
Sr. No. Description (1)SpecifyBCryptPasswordEncoder
in passwordEncoder class.Number of salt hash rounds can be specified as constructor argument. Values from 4 to 31 can be set.Longer the salt value, stronger the hashing. However, as number of calculations increase exponentially, it is necessary to exercise caution from performance perspective.When no value is specified, “10” is set.Tip
It is described later in ‘How to extend’; however, DaoAuthenticationProvider can set the implementation class of
org.springframework.security.crypto.password.PasswordEncoder
as well as the implementation class oforg.springframework.security.authentication.encoding.PasswordEncoder
. Therefore, when the existing PasswordEncoder (authentication package) is changed to a new PasswordEncoder, it can be handled only by changing the passwordEncoder of DaoAuthenticationProvider, after changing the user password.Warning
When
DaoAuthenticationProvider
is set in authentication provider andUsernameNotFoundException
is thrown, without letting the person operating the system know that user does not exist, password is intentionally hashed afterUsernameNotFoundException
is thrown. (Side channel attack countermeasure)To create values for this hashing,
encode
method is once executed internally when starting the application.Warning
When SecureRandom is used in Linux environment, the process may be delayed or timeout may occur. The cause of this issue is random number generation and is described in the following Java Bug Database.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6202721
It has been corrected in the JDK 7 version b20 and above.
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6521844
This issue can be avoided by setting the following as JVM boot arguments.
-Djava.security.egd=file:/dev/./urandom
Java class
@Inject PasswordEncoder passwordEncoder; // (1) public String register(Customer customer, String rawPassword) { // omitted // Password Hashing String password = passwordEncoder.encode(rawPassword); // (2) customer.setPassword(password); // omitted } public boolean matches(Customer customer, String rawPassword) { return passwordEncoder.matches(rawPassword, customer.getPassword()); // (3) }
Sr. No. Description (1)InjectPasswordEncoder
for which bean definition is carried out.(2)Password hashing exampleBy specifying the plaintext password as an argument of encode method, hashed password is returned.(3)Password verification exampleBy specifying plaintext password as the first argument and hashed password as the second argument,‘matches’ method checks whether both the passwords match.
6.4.2.2. StandardPasswordEncoder¶
StandardPasswordEncoder
uses SHA-256 as the hashing algorithm and performs 1024 rounds of stretching.encode(String rawPassword)
method and matches(String rawPassword, String encodedPassword)
methodStandardPasswordEncoder
are described below.encode(String rawPassword) method
matches(String rawPassword, String encodedPassword) method
6.4.2.2.1. Configuration example of StandardPasswordEncoder¶
applicationContext.xml
<bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"> <!-- from properties file --> <constructor-arg value="${passoword.encoder.secret}"/> <!-- (1) --> </bean>
Sr. No. Description (1)Specify the secret key for hashing.When specified, password is hashed with “internally generated salt” + “specified secret key” + “password”.It is recommended to specify secret key, as the strength against rainbow table attack reduces if not specified.About secret keySecret key should be handled as confidential information.Therefore, instead of specifying it directly in the Spring Security configuration file, fetch it from properties file or environment variable etc.Here, example of fetching the secret key from properties file is enabled. Further, care should be taken regarding the storage location of properties file in a production environment.Tip
When secret key is fetched from environment variables
It can be fetched by performing the following settings in
<constructor-arg>
of StandardPasswordEncoder bean definition.<bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"> <!-- from environment variable --> <constructor-arg value="#{systemEnvironment['PASSWORD_ENCODER_SECRET']}" /> <!-- (1) --> </bean>
Sr. No. Description (1)Fetch value from environment variable PASSWORD_ENCODER_SECRET.Refer to Configuration example of BCryptPasswordEncoder, as example of Java class is the same asBCryptPasswordEncoder
.
6.4.2.3. NoOpPasswordEncoder¶
NoOpPasswordEncoder
is the encoder that returns the specified value as a string without any change.6.4.3. How to extend¶
PasswordEncoder
mentioned above.PasswordEncoder
mentioned above, does not fulfill the requirements.- For example we may consider a case wherein the existing hashing system is as follows:
- Algorithm used is SHA-512.
- There are 1000 rounds of stretching.
- Salt is stored in account table column and needs to be passed externally from
PasswordEncoder
.
org.springframework.security.authentication.encoding.PasswordEncoder
of a different packageorg.springframework.security.crypto.password.PasswordEncoder
.Warning
In versions prior to Spring Security 3.1.4, the class that implements
org.springframework.security.authentication.encoding.PasswordEncoder
was used for hashing. However, it has been deprecated from Spring Security version 3.1.4 onwards. Therefore, it differs from the pattern recommended by Spring.
6.4.3.1. Example where ShaPasswordEncoder is used¶
applicationContext.xml
<bean id ="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"> <!-- (1) --> <constructor-arg value="512" /> <!-- (2) --> <property name="iterations" value="1000" /> <!-- (3) --> </bean>
Sr. No. Description (1)Specifyorg.springframework.security.authentication.encoding.ShaPasswordEncoder
as the passwordEncoder.The class to be specified in passwordEncoder should change according to the algorithm to be used.(2)Set the SHA algorithm type as constructor argument.The values “1, 256, 384, 512” can be set. When omitted, “1” is set.(4)Specify the number of stretching rounds at the time of hashing.When omitted, it is 0.spring-mvc.xml
<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> <!-- omitted --> <property name="saltSource" ref="saltSource" /> <!-- (1) --> <property name="userDetailsService" ref="userDetailsService" /> <property name="passwordEncoder" ref="passwordEncoder" /> <!-- (2) --> </bean> <bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource"> <!-- (3) --> <property name="userPropertyToUse" value="username" /> <!-- (4) --> </bean>
Sr. No. Description (1)When salt is to be defined externally, set BeanId of the class that implementsorg.springframework.security.authentication.dao.SaltSource
.In this example,org.springframework.security.authentication.dao.ReflectionSaltSource
that fetches the value set in user information class by reflection, is defined.(2)Specifyorg.springframework.security.authentication.encoding.ShaPasswordEncoder
as the passwordEncoder.The class to be specified in passwordEncoder should change according to the algorithm to be used.(3)Specifyorg.springframework.security.authentication.dao.SaltSource
that decides how to create salt.Here,ReflectionSaltSource
resource that fetchesUserDetails
object property by reflection, is used.(4)username
property ofUserDetails
object is used as salt.Java class
@Inject PasswordEncoder passwordEncoder; public String register(Customer customer, String rawPassword, String userSalt) { // omitted String password = passwordEncoder.encodePassword(rawPassword, userSalt); // (1) customer.setPassword(password); // omitted } public boolean matches(Customer customer, String rawPassword, String userSalt) { return passwordEncoder.isPasswordValid(customer.getPassword(), rawPassword, userSalt); // (2) }
Sr. No. Description (1)To hash password,specify password and salt string as the argument ofencodePassword
methodof the class that implementsorg.springframework.security.authentication.encoding.PasswordEncoder
.(2)To verify password,UsingisPasswordValid
method, hashed password, plain text password andsalt string are specified as the argument and the hashed password and plaintext passwords are compared.
6.4.4. Appendix¶
Note
About stretch
By repeating the hash function computation, information regarding password to be stored can be repeatedly encoded. This is done to prolong the time required to crack a password, and thus acts as a countermeasure against the brute force attack. However, since stretching affects system performance, it is necessary to decide the stretch count on considering the system performance.
Note
About salt
Salt is the string assigned to the original data to be encoded. By assigning salt to a password, the length of the password is increased and thus makes it difficult to crack passwords using rainbow attacks etc. Further, if the same salt is used for multiple users and if there are users who have the same password, it will be obvious from the hash value that same password is used. Therefore, it is recommended to use a different salt (random value etc.) for each user.