Spring security with Hibernate, store encrypted passwords - spring

I'm sure this has been asked before, but I can't find anything that answers this problem.
With Spring-security, I'm using a password encoder.
<beans:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
<authentication-manager>
<authentication-provider user-service-ref='CustomUserDetailsService'>
<password-encoder ref="passwordEncoder"/>
</authentication-provider>
</authentication-manager>
Within my UserDAOImpl I have the following code when adding a user...
#Override
public void addUser(final User user) {
user.setPassword(passwordEncoder.encodePassword(user.getPassword(), "salt"));
sessionFactory.getCurrentSession().save(user);
}
My password gets encoded correctly, but always gets read as invalid, which sort of makes sense as I don't know how Spring would know my salt was "salt" - how do you tell spring security as well as Hibernate to use the same salt? Am I missing something about how spring security manages passwords?

The recommended way is to use a standard password encoder, that will use a random salt, ans store this salt with the digested password. This way, you don't need to provide any salt. If you want to provide your own salt, then you need to inject a SaltSource into the DAO authenticator, as explained by the documentation (and of course use the same source when you encode the password to create a new user):
The StandardPasswordEncoder in the crypto package uses a random 8-byte
salt, which is stored in the same field as the password.
Note
The legacy approach to handling salt was to inject a SaltSource into
the DaoAuthenticationProvider, which would obtain a salt value for a
particular user and pass it to the PasswordEncoder. Using a random
salt and combining it with the password data field means you don't
have to worry about the details of salt handling (such as where the
the value is stored), as it is all done internally. So we'd strongly
recommend you use this approach unless you already have a system in
place which stores the salt separately.
In your case, the SaltSource would always return the "salt". Note that this way of salting is insecure, because all the users sharing a common password (yes, it happens) end up with the same hashed password. This means that an attacker finding the password of one user also finds the password of all the users sharing the same password.

Related

How can I pre-generate a BCrypt hashed password for my Spring Boot application?

I have a Spring Boot application (code here) with a security configuration that utilizes a BCryptPasswordEncoder:
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
I'd like to pre-generate a couple of passwords to initialize my database, for testing or for logging in on a developer machine. (Not for production.) My database is PostgreSQL and the schema is based on the Spring Security default schema, with a users table and an authorities table. My SQL statement looks like this:
insert into users (username, password, enabled) values ('joe','$2y$12$XodbOuISPCPQijlY8MIRUepDeURhxDe09/4VQU0Cno5zkTEKjZouO',true);
I don't know much about how the BCrypt hashing algorithm works, but I generated this password hash (for the password "test") using a free online BCrypt hash generator that looks legitimate. Nevertheless, I cannot log in to my Spring Boot application. The error in the logs is "bad credentials". What gives?
PS: This is a follow-up to this other question.
You can use online BCrypt generator but the thing is that the online generator might generate different regex from your Spring Segurity enconder.
For example the online generator can generate BCrypt with regex “$2y” and your Spring Boot enconder generate with “$2a” regex. If this happen you will get always bad credencials.
I strongly recommend you to generate your passwords using Spring Boot BCrypt Enconder.
#SpringBootApplication
public class QuartzJdbcJobStoreBciApplication extends SpringBootServletInitializer{
public static void main(String[] args {
SpringApplication.run(QuartzJdbcJobStoreBciApplication.class, args);
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password [] = {"Password1", "Password2", "Password3"};
for(int i = 0; i < password.length; i++)
System.out.println(passwordEncoder.encode(password[i]));
}
}
The problem turned out to be the prefix $2y in the hash. This is supposed to represent a version of the BCrypt algorithm but, according to Wikipedia, the prefix is not standard. To be clear, that online generator isn't using a non-standard algorithm, just a non-standard label.
Incidentally, the next section of the hash, $12, indicates the number of rounds of hashing, and even though it's not the same as the Spring default (10 rounds), it doesn't cause the problem.
The solution is to simply change the y for an a. $2a is the standard prefix for a BCrypt hash. You don't need to find a different BCrypt generator or anything, just edit the string.
This works:
insert into users (username, password, enabled) values ('joe','$2a$12$XodbOuISPCPQijlY8MIRUepDeURhxDe09/4VQU0Cno5zkTEKjZouO',true);

Does Spring Security's RunAsManagerImpl work?

Is this a bug in Spring Security's RunAsManagerImpl, or are my expectations wrong?
My understanding of the (limited) documentation, is that with a RunAsManagerImpl defined in my config if I call doFoo() in the following:
#Secured({"ROLE_FOO", "RUN_AS_BAR"})
public void doFoo() {
doBar();
}
#Secured("ROLE_BAR")
public void doBar() {
// ...
}
then, provided the current Authentication has the role "FOO", doBar() will execute successfully.
But it doesn't, Spring throws an AccessDeniedException. However, changing doBar()'s annotation to:
#Secured("ROLE_RUN_AS_BAR")
works successfully.
Upon examination of the source code, the reason is fairly clear - if it encounters an attribute that starts with "RUN_AS_", it creates:
GrantedAuthority extraAuthority = new SimpleGrantedAuthority(getRolePrefix() + attribute.getAttribute());
where, by default:
private String rolePrefix = "ROLE_";
So the authority that is applied is "ROLE_RUN_AS_BAR", which doesn't seem right at all. Is this a bug that I should raise, or have I misunderstood the intended use of this functionality?
It's the expected behavior, as described in the documentation:
The created GrantedAuthorityImpls will be prefixed with a special
prefix indicating that it is a role (default prefix value is ROLE_),
and then the remainder of the RUN_AS_ keyword. For example, RUN_AS_FOO
will result in the creation of a granted authority of ROLE_RUN_AS_FOO.
The purpose of such basic implementation is not to impersonate a user, but to acquire a "technical role". For example, some part of your code should require a technical role of "database manager". No user has this role but I can be acquired programmatically.
Of course, you can bypass this code by just updating the Authentication in SecurityContextHolder, but having a central implementation point to "upgrade" an Authentication object can be more secure when used by a jvm securitymanager.
However, the RunAsManager is a really simple interface, in order to be easily reimplemented: If the default behavior doesn't match what you need, you only have one method to reimplement.

How to handle DTOs for Request, Response and Entities in Spring MVC

I'm a little bit unsure about how to design a complex Spring MVC application in the best way.
The problem is related to a usermanagement system. My UserVO implements the UserDetails interface of Spring.
But for request and response only a special part of information is needed.
In the request it should be allowed to send the password in order to change it. But flags like 'enabled', 'expired', 'locked' should obviously not be changable by the user.
On the other side this information should be displayed to the user, so must be included in the Response. The password is never send to the client.
I started with using the JsonIgnore and JsonAttribute Annotations on setter- and getter. But as the flags are boolean they are persisted with the default value 'false' every time I update.
Possible solution: Writing a DTO for response as well as for the request and using the ObjectMapper of Spring to persist them. Is this the right approach? I would feel more comfortable if I could just work with my VOs and set some magic annotations if you know what I mean ;)
You probably need a form-backing bean. You will find a ton of examples on internet.
EDIT :
Example with user. Your form contains the username, a field to change the password and a field to reconfirm the password.
The username is in you User POJO wich contains all the data relative to your user.
To catch the password and password confirm from your form (and all other data that you expose from your User POJO you need a form-backing bean).
public class UserBacking {
private String newPsw;
private String confirmPsw;
private User user;
}
In your form
<form:form action="${postUrl}" commandName="userBacking " method="POST">
<!-- Fields goes here -->
</form>
In your Controller, your method will receive as ModelAttribute the UserBacking object.

How to change password hashing algorithm when using spring security?

I'm working on a legacy Spring MVC based web Application which is using a - by current standards - inappropriate hashing algorithm. Now I want to gradually migrate all hashes to bcrypt. My high level strategy is:
New hashes are generated with bcrypt by default
When a user successfully logs in and has still a legacy hash, the app replaces the old hash with a new bcrypt hash.
What is the most idiomatic way of implementing this strategy with Spring Security? Should I use a custom Filter or my on AccessDecisionManager or …?
You'll probably have to customize your AuthenticationProvider since that is where the password is actually compared with the user data and you have all the information you need available.
In the authenticate method, you would first load the user data. Then check the user-supplied password with both a BCryptPasswordEncoder and your legacy one. If neither returns a match, throw a BadCredentialsException.
If the user authenticates successfully (very important :-)) and the password is legacy format (the legacy encoder matched), you would then call some additional code to update the user's account data and replace the legacy hash with a bcrypt one. The BCryptPasswordEncoder can be also be used to create new hashes.
If you want, you could detect in advance whether the stored hash was already bcrypt before doing the comparisons. Bcrypt strings have quite a distinct format.
Note also that to make it harder to guess valid account names, you should try to make the method behave the same both when a supplied username exists and when it doesn't (in terms of the time it takes). So call the encoders even when you don't have any user data for the supplied username.
i think best way to do this is to specify password encoder to authentication provider some thing like below, for more information refer doc
<authentication-manager>
<authentication-provider user-service-ref="userService">
<password-encoder ref="passwordEncoder">
<salt-source ref="saltSource" />
</password-encoder>
</authentication-provider>
</authentication-manager>
<beans:bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"
id="passwordEncoder" />
<beans:bean class="org.springframework.security.authentication.dao.ReflectionSaltSource"
id="saltSource">
<beans:property name="userPropertyToUse" value="userName" />
</beans:bean>

Implementing remember me without a key

i found some samples that implements remember me functionality by just
<remember-me/>
and other samples implement it as:
<remember-me key="_spring_security_remember_me"/>
and i want to know what is the difference between the two declarations, and is the _spring_security_remember_me is a predefined key?
thanks.
The default key can be found in AuthenticationConfigBuilder.createRememberMeFilter()
final String DEF_KEY = "SpringSecured";
That is the value that is used if you don't specify one in <remember-me>
From the documentation, the key attribute is used in hashing the value stored in the cookie. It prevents a malicious user from trying to decode the cookie, because they can't do that (well it s a lot harder) without the key.
For anyone looking for the rememberme().key() feature in the future, it seems that as of Spring Boot 2.2.6 there is SecureRandom generator to generate the key if it is not provided. Here is the implementation found in org.springframework.security.config.http.AuthenticationConfigBuilder.createRememberMeFilter
private String createKey() {
SecureRandom random = new SecureRandom();
return Long.toString(random.nextLong());
}

Resources