Password Hashing in Broadleaf 4.0 (SHA-256)
Posted: Mon Jul 13, 2015 5:05 am
Hi,
I was struggling with setting up Salted SHA-256 passwords for customers. With salt as emails.
I found out some stuff to make it work. Thought of sharing so it might help others.
1. CHANGES IN applicationContext-security.xml
a. Setup salt-source: i.e. which property should be used as salt. I wanted it to be username [which is same as email by default]. You are free to use your own customer property.
NOTE: If you want your own salt [same for all users, you can skip this step]
b. Define a bean for your password encoder: I used SHA-256 algorithm.
c. Configure Authentication Manager to use your encoder and salt: Third line in the below code refers to the bean defined in step (b). Fourth line in the below code refers to the bean being used for salting.
If you want a common salt for all users, you can use the below code:-
2. CREATE A UTILITY CLASS FOR PROVIDING ShaPasswordEncoder Object: This will be used for hashing the password entered by the user on front-end for validation.
3. OVERRIDE CustomerServiceImpl.registerCustomer() in CORE MODULE: This class uses the above utility class and encodes the password before registration.
In applicationContext.xml, register this new class with the original bean id [which belonged to CustomerServiceImpl]
4. RegisterController CUSTOMIZATION: This is required because of a tricky bug in broadleaf 4.0
The bug is, when a user registers, the password is set as the hashed one. [WHICH IS OK]
NEXT, the login service is called. During the login, the password is picked up from the database [which is already hashed].
As a part of authentication, this is hashed again and there is a password mismatch.
Ideally, under normal scenario, during login the user will enter a plain password and will be hashed once to validate. In this case, the hashed password is picked from database and is re-hashed, which fails the authentication.
I copied the code from BroadleafRegisterController's processRegister method into RegisterController's processRegister method.
a. At the first line of processRegister method add the below code
b. Replace the following code
BY
Now, for all the operations, the password will be hashed.
NOTE: This is the process for hashing passwords by SHA-256 algorithm, which is salted by the user's email-id on user-facing website and NOT admin panel.
____________________________________________________________________________
HOW TO RESET PASSWORD IF FORGOTTEN: In case you lose your password and you need to quickly reset it from database, you can use the following process to do so.
1. Choose a password in clear text: For example, you want to set your password as Magic2015
2. Next, choose a salt: It can be a user-property or a simple text value. For e.g. we want to use the salt as abc@def.com
3. Now, place your string in the format: <password_in_clear_text>{<your_salt>}. In our case, the string will be Magic2015{abc@def.com}
4. Use any online utility to hash this string using SHA-256 algorithm. The hashed string of above string will be 9f9c9b9682bd06a01cc47fed8929d70a4751bd011ccb36df82ecac68b65bd4a3
5. Insert this into database.
6. Now, you can login using credentials abc@def.com as username and Magic2015 as password
I was struggling with setting up Salted SHA-256 passwords for customers. With salt as emails.
I found out some stuff to make it work. Thought of sharing so it might help others.
1. CHANGES IN applicationContext-security.xml
a. Setup salt-source: i.e. which property should be used as salt. I wanted it to be username [which is same as email by default]. You are free to use your own customer property.
NOTE: If you want your own salt [same for all users, you can skip this step]
Code: Select all
<bean id="blSaltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<property name="userPropertyToUse" value="username" />
</bean>
b. Define a bean for your password encoder: I used SHA-256 algorithm.
Code: Select all
<bean id="mdPasswordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg value="256"/>
</bean>
c. Configure Authentication Manager to use your encoder and salt: Third line in the below code refers to the bean defined in step (b). Fourth line in the below code refers to the bean being used for salting.
Code: Select all
<sec:authentication-manager alias="blAuthenticationManager">
<sec:authentication-provider user-service-ref="blUserDetailsService">
<sec:password-encoder hash="sha-256" ref="mdPasswordEncoder">
<sec:salt-source ref="blSaltSource" />
</sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
If you want a common salt for all users, you can use the below code:-
Code: Select all
<sec:authentication-manager alias="blAuthenticationManager">
<sec:authentication-provider user-service-ref="blUserDetailsService">
<sec:password-encoder hash="sha-256" ref="mdPasswordEncoder">
<sec:salt-source system-wide="--your-salt-string--" />
</sec:password-encoder>
</sec:authentication-provider>
</sec:authentication-manager>
2. CREATE A UTILITY CLASS FOR PROVIDING ShaPasswordEncoder Object: This will be used for hashing the password entered by the user on front-end for validation.
Code: Select all
public class CommonUtils {
private static final ShaPasswordEncoder encoder = new ShaPasswordEncoder(256);
public static ShaPasswordEncoder getCustomerPasswordEncoder() {
return encoder;
}
}
3. OVERRIDE CustomerServiceImpl.registerCustomer() in CORE MODULE: This class uses the above utility class and encodes the password before registration.
Code: Select all
public class MDCustomerServiceImpl extends CustomerServiceImpl {
@Override
public Customer registerCustomer(Customer customer, String password, String passwordConfirm) {
ShaPasswordEncoder encoder = CommonUtils.getCustomerPasswordEncoder();
password = encoder.encodePassword(password, customer.getUsername());
passwordConfirm = encoder.encodePassword(passwordConfirm, customer.getUsername());
return super.registerCustomer(customer, password, passwordConfirm);
}
}
In applicationContext.xml, register this new class with the original bean id [which belonged to CustomerServiceImpl]
Code: Select all
<bean id="blCustomerService" class="<path_to_above_class>.MDCustomerServiceImpl" />
4. RegisterController CUSTOMIZATION: This is required because of a tricky bug in broadleaf 4.0
The bug is, when a user registers, the password is set as the hashed one. [WHICH IS OK]
NEXT, the login service is called. During the login, the password is picked up from the database [which is already hashed].
As a part of authentication, this is hashed again and there is a password mismatch.
Ideally, under normal scenario, during login the user will enter a plain password and will be hashed once to validate. In this case, the hashed password is picked from database and is re-hashed, which fails the authentication.
I copied the code from BroadleafRegisterController's processRegister method into RegisterController's processRegister method.
a. At the first line of processRegister method add the below code
Code: Select all
String unencodedPassword = registerCustomerForm.getPassword(); //store non-hashed password in a variable
b. Replace the following code
Code: Select all
loginService.loginCustomer(registerCustomerForm.getCustomer());
BY
Code: Select all
//#### Store hashed password in a variable
String codedPassword = registerCustomerForm.getCustomer().getPassword();
//#### SET UNENCODED PASSWORD AS PLAIN ONE FROM ABOVE
newCustomer.setUnencodedPassword(unencodedPassword);
//#### REPLACE WITH "newCustomer" for loginService.
loginService.loginCustomer(newCustomer);
//AFTER LOGIN RE-SET THE HASHED PASSWORD - Because loginService resets it to unencodedPassword.
newCustomer.setUnencodedPassword(codedPassword);
newCustomer.setPassword(codedPassword);
customerService.saveCustomer(newCustomer);
Now, for all the operations, the password will be hashed.
NOTE: This is the process for hashing passwords by SHA-256 algorithm, which is salted by the user's email-id on user-facing website and NOT admin panel.
____________________________________________________________________________
HOW TO RESET PASSWORD IF FORGOTTEN: In case you lose your password and you need to quickly reset it from database, you can use the following process to do so.
1. Choose a password in clear text: For example, you want to set your password as Magic2015
2. Next, choose a salt: It can be a user-property or a simple text value. For e.g. we want to use the salt as abc@def.com
3. Now, place your string in the format: <password_in_clear_text>{<your_salt>}. In our case, the string will be Magic2015{abc@def.com}
4. Use any online utility to hash this string using SHA-256 algorithm. The hashed string of above string will be 9f9c9b9682bd06a01cc47fed8929d70a4751bd011ccb36df82ecac68b65bd4a3
5. Insert this into database.
Code: Select all
insert into BLC_CUSTOMER
set password = '9f9c9b9682bd06a01cc47fed8929d70a4751bd011ccb36df82ecac68b65bd4a3'
where email = 'abc@def.com';
6. Now, you can login using credentials abc@def.com as username and Magic2015 as password