Best way for spring boot password encryption not using BCryptPasswordEncoder? - spring

How can i encrypt user login password apart from BCryptPasswordEncoder.
Suppose I'm not using this dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

BCryptPasswordEncoder is just another encoder. Spring provides implementations of many such encoders. You can use implementations of different one way hash algorithms like SHA-256, SHA-512 etc. Java provides implementations of the same in java.security package. Check java.security.MessageDigest class.
One advantage of using BCryptPasswordEncoder like encoders is that you do not need to generate random password salt yourself. It takes care of it and uses random salt implicitly and that's why generates different encoded string every time for the same Plain text.

After searching I figured out that best practice is to use encoder as Chetan Ahirrao
said.
the solution i found is to add
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.7.3</version>
</dependency>
and use BCryptPasswordEncoder
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder(10);
}

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);

How to pass MultipartFile in requestBody for a spring boot rest controller

So I have seen examples where a MultiPartFile type is passed in #RequestParam and not in #RequestBody. That seems to be a very usual way people suggest to consume a file content in a #RestController something like this
public ResponseEntity<String> submitFile(#RequestParam(value="file") MultipartFile file)
I am wondering how is it a good practice as the file data gets passed in the url. Why not pass it in #RequestBody instead?
So I changed the above code to something like this
public ResponseEntity<String> submitFile(#RequestBody MyCustomObj myObj)
myCustomObj is a pojo with just one field named file of type MultipartFile
The problem is that I only have swagger and postman to test it and when I use the #RequestBody approach, none of these would give me an option to upload a file as they would in case of passing MultipartFile in RequestParam.
Can someone please throw some more light on this and tell me the right way to do this?
As an alternative and based on your comments I would recommend you take a look at the community project called Spring Content. This provides a resource abstraction over storage giving flexibility to where your content is stored and it injects the service and controller implementations for you so that you don't need to implement either yourself. Also, as you mentioned it might become important, Spring Content allows you to associate uploaded content with Spring Data entities too.
Adding it to your project would look something like this:
pom.xml (assuming maven. Spring boot starters also available)
<!-- Java API -->
<!-- just change this depdendency if you want to store somewhere else -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-fs</artifactId>
<version>0.8.0</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>0.8.0</version>
</dependency>
StoreConfig.java
#Configuration
#EnableFilesystemStores
#Import(RestConfiguration.class)
public class StoreConfig {
#Bean
FileSystemResourceLoader fileSystemResourceLoader() throws IOException {
return new FileSystemResourceLoader(new File("/path/to/uploaded/files").getAbsolutePath());
}
}
FileStore.java
#StoreRestResource(path="files")
public interface FileStore extends Store<String> {
}
And that's it. The FileStore is essentially a generic Spring ResourceLoader. The spring-content-fs dependency will cause Spring Content to inject a filesystem-based implementation. The spring-content-rest dependency will cause Spring Content to also inject an implementation if an #Controller that forwards HTTP requests onto the methods of the FileStore service.
So you will now have a fully functional (POST, PUT, GET, DELETE) REST-based file service at /files that will use your FileStore to retrieve (and store) files in /path/to/uploaded/files on your server.
So:
curl --upload-file some-image.jpg /files/some-image.jpg
will upload some-image.jpg and store it in /path/to/uploaded/files on your server.
And:
curl /files/some-image.jpg
will retrieve it again.
HTH
The injected controller also supports video streaming too, in case that is useful.
Later on down the line if/when you want to associate content with a Spring Data entity all you would need to do is make your FileStore extend ContentStore instead of Store, type it to the Spring Data entity that you are associating with and add the Spring Content annotations to your entity, as follows:
//#StoreRestResource(path="files") <-- no longer required
public interface FileStore extends ContentStore<YourEntity, String> {
}
#Entity
public class YourEntity {
#Id
...
#ContentId
private String contentId;
#ContentLength
private String contentLen;
#MimeType
private String contentType;
}
And that's it. As you might expect your REST endpoints change so that you now address content using the same URI space as your Spring Data entity. So:
curl --upload-file some-image.jpg /yourEntities/{yourEntityId}
will upload some-image.jpg, store it in /path/to/uploaded/files on your server and associate it with the entity yourEntityId.
And:
curl /yourEntities/{yourEntityId}
will retrieve it again.
Multiple pieces of content can be associated by using conventional #OneToOne and #OneToMany associations and are reflected accordingly in the URI in a (hopefully) intuitive way.
HTH
#RequestParam maps to query parameters, form data, and parts in multipart requests and not only query parameters as mentioned the offical docs.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html
Files are not supposed to be sent as the request body serialized in JSON.
What you should do instead is to use the content type "multipart/form-data" for your file uploads (as mentioned in the HTML 4 spec below) and in that case #RequestParam will be the appropriate annotation to use
https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

How can I do bean-validation with spring repositories?

I'm trying to use my repository interface looks like this.
interface SomeRepository extends JpaRepository<Some, Long> {
#org.springframework.lang.Nullable
Some findByKey(
#org.springframework.lang.NonNull
#javax.validation.constraint.NotNull
final String key);
}
And I found those constraints don't work as expected.
#Test
void findByKeyWithNullKey() {
repository.findByKey(null);
}
The test case simply passes.
How can I make it work?
According to Spring JPA document :
To enable runtime checking of nullability constraints for query methods, you need to activate non-nullability on the package level by using Spring’s #NonNullApi.
you can add package annotations simply by creating package-info.java file and add the package declaration that it relates to in the file.Then add this annotation to your package like so :
#org.springframework.lang.NonNullApi
package com.example;
I would suggest to use javax validation in your spring framework and suppose if you are using maven so you just have to include below dependency
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
after that please try below code
Some findByKey(
#NotNull final String key);
It works as you pasted the code. Of course, you need to use #Repository on the repo and remove #javax.validation.constraint.NotNull since that's not what you want. Furthermore, you need to make sure you have the proper imports in the pom.
I'd recommend doing the reverse, adding non null api on package level, then:
Rule findOneByExpression(#Nullable String expression);
ruleRepository.findOneByExpression(null);
And see it fail, if it returns null. Then change it like so:
#Nullable
Rule findOneByExpression(#Nullable String expression);
And it will pass.

Spring security with Hibernate, store encrypted passwords

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.

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