Hashing passwords in Spring applications | Nullbeans (2024)

When handling account passwords, you need to make sure that passwords are transmitted through a secure medium and stored in a persistent and a non-reversibly hashed format.

A large majority of backend and web-based applications rely on authorization mechanisms which involve the user inserting a username and a password. When building such applications for the first time, an important question comes to mind. How to manage user passwords in a secure way?

In this post, we will discuss how to manage passwords and the different steps that you need to take when you are building Java or spring based applications.

Contents hide

1 Storing passwords in a database

2 Hashing passwords

4 Summary and further reading

4.1 References

5 Related Posts

Storing passwords in a database

The first thing that you need to decide when handling authentication data is where to save the user account passwords. Traditionally, you save this data in a database. This approach has worked for many years and will still work to this day.

Depending on the stack you are using, you could be saving your passwords in a relational database such as a Postgres DB or Oracle, or you could also store them on a NoSQL database such as MongoDB.

When storing passwords in a database, you should make sure that the connectivity between your application and the database is secure. Otherwise, all other security measures will not matter.

This means that you should be using mechanisms such as SSL to ensure the security of the transmitted data between your application and the database. Otherwise, anyone eavesdropping on your network traffic can figure out the contents of your data.

Hashing passwords

Now that you have decided to save your user accounts’ passwords in a database, you will also need to decide the format in which the passwords will be stored. You could store passwords in plain-text format. For example, a password such as “Password1234” will look like “Password1234” when you see it in the database.

However, I would like to stress that storing passwords in plain text format in your database can be a very bad idea, and for several reasons:

  • If your database is compromised for any reason, then the passwords of all the users in your database are also compromised. This means that the hacker can now impersonate one of your users in your system, without you knowing about it.
  • People often use the same password in more than one platform. For example, a user could use the same password on your system and on their Facebook account. So if your database is compromised, you could be risking not only accounts saved in your system, but also user accounts on other platforms.

Here is where hashing comes in. The idea is simple. Instead of storing the plain-text passwords, use a hashing function to hash the plain-text passwords, and store the hashed values in your database instead.

So how would you authenticate users? When a user is logging in, you do not need to match the input password with the stored value. Instead, you will hash the inserted password during login, and compare the output with the stored hashed value. If the values match, then the user has entered the correct password and is allowed to proceed.

This makes it very difficult to impersonate a user even when the hashed values are exposed as an attacker will still need the input value (the password) to the hashing function in order to produce a matching hash value.

Hashing is the process of applying a mathematical function (called a hashing function) to a piece of data in order to produce an output value. While we will not go too deep into detail about hashing, you will need to keep in mind a few things when choosing a hashing function:

  • Hashing functions should go in one way only. This means that if you decide to hash a value, then there is no mathematical way to “unhash” it to produce the original input.
  • Collision resistance. A collision happens when two or more different input values produce the same output when passed through the hashing function. Naturally you will want to avoid that if you want to build a secure system.
  • Hashing with salt. You will also need to choose a hashing algorithm that utilizes a “salt” value.
    • A salt value is a random value that is provided to the hashing function as an additional input. This allows you to store two input values with different hash outputs.
    • For example, if two users decide to use the password “Password”, they will be stored with the same hash value. This allows an attacker to perform a “Rainbow table” attack by comparing the hash value with pre-computed hashes in order to find the user’s input password.
    • If you hash the passwords with an additional salt value which is both unique and random, then even if the two users use the same password, the users will have different hashed values.
    • The randomly generated salt values will need to be saved in order to be used for authenticating the users in their login attempts.
  • Use slow hashing functions. This is important in order to make brute force attacks prohibitive. Ideally, the algorithm that you choose should be fast enough on CPUs but not fast or economical enough on GPUs, FPGAs or ASICs to make a brute force attack possible.
Hashing passwords in Spring applications | Nullbeans (1)

Another additional step that you could take to protect passwords is to apply multiple iterations of hashing to the user’s password. For example, you could use the output of the hashing function as an input to the same (or a different) hashing function. You could do this in as many iterations as you want.

This makes brute force attacks even more difficult, as an attacker will need to try many combinations, with each combination being hashed multiple times, making the brute force attack more expensive and not feasible in a reasonable amount of time.

Of course, the downside is that your application will need more resources if a lot of login attempts are being made in your system.

Implementing hashing in Java & Spring

Thankfully, there are a lot of hashing functionalities that come out of the box with Spring and Java. When hashing passwords, three popular algorithms come to mind. PBKDF2, scrypt and bcrypt.

We will discuss how to implement each of them in Java in order for you to be able to integrate them into your application.

PBKDF2

PBKDF2 stands for Password Based Key Derivative Function. The 2 in the name is there as it is the newer version of the algorithm. In the following implementation, you will not need to import any classpath dependencies. Please note that we used JDK 11 for this example, so your mileage may vary depending on your JDK version.

First, we generate the salt value using the SecureRandom Java class.

 SecureRandom secureRandom = new SecureRandom(); byte[] salt = secureRandom.generateSeed(12);

The next step is to hash the password. We will request 10 iterations from the hashing algorithm and a 512 byte key size.

 PBEKeySpec pbeKeySpec = new PBEKeySpec("password".toCharArray(), salt, 10, 512); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); byte[] hash = skf.generateSecret(pbeKeySpec).getEncoded();

Now that you have the hash, you will need to store it somewhere. If you plan to store it in a database in String format, then you can use Base64 to encode the resulting hash byte array into a string.

 String base64Hash = Base64.getMimeEncoder().encodeToString(hash);

Please also make sure to store the salt value safely in order to be able to regenerate the hash during login attempts.

When a user attempts to authenticate, you can redo the steps described above and test that the stored and the generated hashes are equal. Below you will find the full example code.

import org.junit.jupiter.api.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.PBEKeySpec;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import java.security.spec.InvalidKeySpecException;import java.util.Base64;public class PbkdF2Test { public static final Logger log = LoggerFactory.getLogger(PbkdF2Test.class); @Test public void test() throws NoSuchAlgorithmException, InvalidKeySpecException { SecureRandom secureRandom = new SecureRandom(); //make sure to save this into a database byte[] salt = secureRandom.generateSeed(12); PBEKeySpec pbeKeySpec = new PBEKeySpec("password".toCharArray(), salt, 10, 512); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); byte[] hash = skf.generateSecret(pbeKeySpec).getEncoded(); //converting to string to store into database String base64Hash = Base64.getMimeEncoder().encodeToString(hash); log.info(base64Hash); //Password matching steps String input = "password"; //Here, you obtain the salt from the database PBEKeySpec pbeKeySpec2 = new PBEKeySpec(input.toCharArray(), salt, 10, 512); SecretKeyFactory skf2 = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); byte[] hash2 = skf.generateSecret(pbeKeySpec).getEncoded(); String base64Hash2 = Base64.getMimeEncoder().encodeToString(hash); log.info(base64Hash2); log.info("check if hashes match, result: {}", base64Hash.equals(base64Hash2)); }}

If you attempt to run the code above, you will get something similar to this output.

00:01:37.153 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.PbkdF2Test - CdaihVpWON/2zY5MLccQX6Bl0ru10JaiO5KTOT3T9BEoEIQdz0iH8kuloVYG6onH34ox5q4VVBIEAikO7ZHRSw==00:01:37.158 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.PbkdF2Test - CdaihVpWON/2zY5MLccQX6Bl0ru10JaiO5KTOT3T9BEoEIQdz0iH8kuloVYG6onH34ox5q4VVBIEAikO7ZHRSw==00:01:37.159 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.PbkdF2Test - check if hashes match, result: trueProcess finished with exit code 0

bcrypt

The following example utilizes the power of Spring-Security in order to generate the bcrypt hashes. bcrypt is a password hashing function based on the Blowfish cipher. It has been published in 1999 and has since been a favorite choice among software developers for hashing passwords.

This is because bcrypt can be iteratively applied to passwords in order to offset advances in hardware processing speeds, making it harder to brute-force.

Before you start, make sure you have Spring-security library in your classpath. If you are using Spring-boot, then the dependency will look as follows.

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

If you are not using spring boot, then you can add the required dependency as follows.

 <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>X.X.X</version> </dependency>

Using bcrypt with Spring is quite simple. All you need to do is to start an instance of the BCryptPasswordEncoder. There are two main methods that you will need from the encoder. The encode method, which generates the hash value, and the matches method which compares a password and a bcrypt hash to figure out if the password matches the hashed value. Please find below an example usage.

import org.junit.jupiter.api.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;public class BCryptTest { public static final Logger log = LoggerFactory.getLogger(BCryptTest.class); @Test public void testHashWithSalt(){ BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); String hashedPass1 = bCryptPasswordEncoder.encode("password"); String hashedPass2 = bCryptPasswordEncoder.encode("password"); log.info(hashedPass1); log.info(hashedPass2); log.info("Result of matching: {}" ,bCryptPasswordEncoder.matches("password", hashedPass1)); log.info("Result of matching: {}" ,bCryptPasswordEncoder.matches("password", hashedPass2)); //trying to match with a previously generated value for same password. Should be 'true' log.info("Result of matching: {}" ,bCryptPasswordEncoder.matches("password", "$2a$10$mP.DNH3LIy/PeIM84y1nhuq76w98b8ANcxH3bzxPjiXHUdSl3XFri")); log.info("Result of matching: {}" ,bCryptPasswordEncoder.matches("password", "$2a$10$mP.DNH3LIy/PeIM84y1nhuq77w98b8ANcxH3bzxPjiXHUdSl3XFri")); log.info("Result of matching: {}" ,bCryptPasswordEncoder.matches("paSSword", "$2a$10$mP.DNH3LIy/PeIM84y1nhuq76w98b8ANcxH3bzxPjiXHUdSl3XFri")); }}

If you run the program above, you will have an output similar to this.

00:27:50.520 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - $2a$10$0LeSzg9W/wdMCaVxCjd.CuHu9YEncUTaTI/lHXbVgyBwhVSjbc4RC00:27:50.524 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - $2a$10$MzKLAXdP3F9ci.wtzuLsyelAb39TmPHDCEYkCIx9QfFR9z6GrgjlC00:27:50.641 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - Result of matching: true00:27:50.754 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - Result of matching: true00:27:50.864 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - Result of matching: true00:27:50.972 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - Result of matching: false00:27:51.081 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.BCryptTest - Result of matching: falseProcess finished with exit code 0

Please keep in mind that the salt value is generated automatically by the encoder and is included inside the hash. Also note that you can configure different parameters of the encoder, such as the log rounds to be used, the version of bcrypt and a SecurityRandom for extra security.

scrypt

scrypt is another hashing function, which is password based. It was first published in 2009. Due to memory constraints, large scale hardware brute force attacks against scrypt can be extremely expensive.

In this example, we used Spring-Security as well. However, you will also need to add a dependency to “Bouncy Castle Core”.

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.64</version></dependency>

The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms and it is used by Spring for their implementation of the SCryptPasswordEncoder.

The encoder can be used similar to the bcrypt encoder. Take a look at the code below.

import org.junit.jupiter.api.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;public class ScryptTest { public static final Logger log = LoggerFactory.getLogger(ScryptTest.class); @Test public void test(){ SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder(); sCryptPasswordEncoder.encode("password"); String hashedPass1 = sCryptPasswordEncoder.encode("password"); String hashedPass2 = sCryptPasswordEncoder.encode("password"); log.info(hashedPass1); log.info(hashedPass2); log.info("Result of matching: {}" ,sCryptPasswordEncoder.matches("password", hashedPass1)); log.info("Result of matching: {}" ,sCryptPasswordEncoder.matches("password", hashedPass2)); }}

If you run the code above, you will have the following result.

00:40:30.502 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.ScryptTest - $e0801$kp8Si63et+gMiSqvqRKOQhN6jHzKxM25knb7WGusuch1no0rhezghKXv63Yd2O0VbeJ39PFOSDpIkZalL+D6LQ==$i5m9JjUhfh72d06zKhdW0/80QHQbnsyY0pOJfAMI9gc=00:40:30.505 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.ScryptTest - $e0801$WhhFwotPHmtCuItQqNdd2E4GKliannYtjpVtuWgPSEyJGTGU58/8gS1SVnNbnwPA+TUxRNlJhpDiJEekm9JMgw==$TqeCAJUkgVlpimOhj6Fz/CjcKVTDJytxjKkHVvDnYoo=00:40:30.602 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.ScryptTest - Result of matching: true00:40:30.686 [main] INFO com.nullbeans.customerservice.incidentmanagement.utils.crypto.ScryptTest - Result of matching: trueProcess finished with exit code 0

Notice that the salt values are included in the hashed values. Notice also that the password matched the two hashes, even though they are of different values.

Please also note that the scrypt encoder can be configured with additional parameters, such as the cpu difficulty (cpu cost), memory cost, salt length, among others.

Summary and further reading

If you find securing passwords and storing them safely in databases manually is a demanding task, then you can also use a secrets management engine such as Hashicorp Vault.

The nice thing about Vault is that it is open source, and it can be used not only to store user passwords, but other important data such as database passwords, passwords to external systems, authentication tokens, etc.. But that is a story for another day 🙂

In this post, we discussed why is it important to hash passwords, what are we looking for in hashing functions, and how to perform basic implementations of hashing in Java and Spring.

References

Hashing passwords in Spring applications

Related Posts

  • How to configure OpenFeign with Spring boot
  • How to create a CRUD REST API in Spring Boot
  • How to identify vulnerable dependencies in a Maven project
  • How to configure query parameters in Spring Controllers
  • Error handling in REST and Spring Boot
  • How to use Java Bean Validation in Spring Boot
Hashing passwords in Spring applications | Nullbeans (2024)

FAQs

Is hashing a password enough? ›

But, password hashing alone isn't enough. Password salting adds another layer of security that blocks the most common forms of attack vectors, adding a level of unpredictability to further lower the chance of data breaches.

Does Spring Security support password hashing? ›

In Spring Security, you can use the BCryptPasswordEncoder class to hash passwords using the bcrypt algorithm. The BCrypt algorithm is a popular choice because it is computationally expensive, which makes it more difficult for an attacker to crack the hash.

How should passwords be stored in spring? ›

Instead of using just the password as input to the hash function, random bytes (known as salt) would be generated for every user's password. The salt and the user's password would be run through the hash function to produce a unique hash. The salt would be stored alongside the user's password in clear text.

What is the best method to hash passwords? ›

While Argon2id should be the best choice for password hashing, scrypt should be used when the former is not available. Like Argon2id, scrypt has three different parameters that can be configured: the minimum CPU/memory cost parameter (N), the blocksize (r) and the degree of parallelism (p).

What are the disadvantages of hashing passwords? ›

Limitations of Password Hashing

Hackers can try a brute-force attack by running random passwords through the hash function until they finally find a match. This is rather inefficient since the hash algorithms designed for securely storing passwords are designed to be slow, making the entire process tedious and long.

Is it easy to crack hashed passwords? ›

Yes, having a hash and performing an offline brute force will allow you to be more resource-efficient and brute-force the hash in parallel without any technical limitations imposed by the authentication system. So, sure, it's "easier".

Which algorithm is best for storing passwords? ›

To protect passwords, experts suggest using a strong and slow hashing algorithm like Argon2 or Bcrypt, combined with salt (or even better, with salt and pepper).

Is it better to store passwords in String or char array in Java? ›

Since String is immutable, there is no method defined that allow us to change or overwrite the content of the string. This feature makes string objects unstable for storing secure information such as passwords, SSN, etc. We should always store the secure information in char[] array rather than String.

Should you hash a password multiple times? ›

Cryptography: What is the benefit of hashing a password multiple times? The primary reason is protection against brute force attacks. You're adding a work factor to slow down trials of possible keys.

Which algorithm to hash passwords? ›

SHA-2 functions are now the most commonly used hashing algorithms. Bcrypt. It's a hashing function that produces a 192-bite hash value. Bcrypt is a slow-functioning algorithm that takes time to create password hashes and decrypt them, making it less susceptible to dictionary-based cyberattacks.

Is SHA-256 better than MD5? ›

SHA256 has several advantages over MD5 and SHA-1, such as producing a longer hash (256 bits) that is more resistant to collisions and brute-force attacks. Additionally, there are no known vulnerabilities or weaknesses with SHA256, unlike MD5 and SHA-1 which have been exploited by hackers and researchers.

Are hashed passwords vulnerable? ›

Hash collisions take place when two different inputs produce the same hash output. While rare, hash collisions pose a security risk as an attacker could deliberately create a different password that generates the same hash as the original password, allowing them to gain unauthorized access.

Can hashed passwords be decrypted? ›

Hashing is a one-way encryption of the password — with one-way simply meaning that once encrypted the data cannot be decrypted.

How secure is hashing? ›

Data security: A hashed value is virtually useless to cybercriminals and bad actors because it is extremely challenging to decode a one-way hash function. This means that a data breach may not necessarily result in the loss of sensitive data if it has been properly hashed.

Top Articles
How to hire the best travel insurance to United States
100+ Ways to Make Money Farming That are Perfect for 2024
Devotion Showtimes Near Xscape Theatres Blankenbaker 16
My Arkansas Copa
Po Box 7250 Sioux Falls Sd
Stretchmark Camouflage Highland Park
Herbalism Guide Tbc
Degreeworks Sbu
Seafood Bucket Cajun Style Seafood Restaurant in South Salt Lake - Restaurant menu and reviews
D10 Wrestling Facebook
Kris Carolla Obituary
VMware’s Partner Connect Program: an evolution of opportunities
Craftology East Peoria Il
Theresa Alone Gofundme
Po Box 35691 Canton Oh
Jinx Chapter 24: Release Date, Spoilers & Where To Read - OtakuKart
Roll Out Gutter Extensions Lowe's
Divina Rapsing
Kp Nurse Scholars
Scotchlas Funeral Home Obituaries
Ubg98.Github.io Unblocked
Craigslist Prescott Az Free Stuff
Craigslist Lakeville Ma
Ppm Claims Amynta
LCS Saturday: Both Phillies and Astros one game from World Series
Red Cedar Farms Goldendoodle
Olivia Maeday
Kohls Lufkin Tx
Ewg Eucerin
LG UN90 65" 4K Smart UHD TV - 65UN9000AUJ | LG CA
Progressbook Newark
Craigslist/Phx
What Is The Lineup For Nascar Race Today
Current Time In Maryland
LEGO Star Wars: Rebuild the Galaxy Review - Latest Animated Special Brings Loads of Fun With An Emotional Twist
Lichen - 1.17.0 - Gemsbok! Antler Windchimes! Shoji Screens!
Junior / medior handhaver openbare ruimte (BOA) - Gemeente Leiden
Craigslist Pets Huntsville Alabama
Ktbs Payroll Login
Conroe Isd Sign In
Atlanta Musicians Craigslist
Yogu Cheshire
Why I’m Joining Flipboard
Omaha Steaks Lava Cake Microwave Instructions
Flipper Zero Delivery Time
The best specialist spirits store | Spirituosengalerie Stuttgart
'The Night Agent' Star Luciane Buchanan's Dating Life Is a Mystery
Spreading Unverified Info Crossword Clue
Msatlantathickdream
Elizabethtown Mesothelioma Legal Question
Yoshidakins
Latest Posts
Article information

Author: Amb. Frankie Simonis

Last Updated:

Views: 5547

Rating: 4.6 / 5 (56 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Amb. Frankie Simonis

Birthday: 1998-02-19

Address: 64841 Delmar Isle, North Wiley, OR 74073

Phone: +17844167847676

Job: Forward IT Agent

Hobby: LARPing, Kitesurfing, Sewing, Digital arts, Sand art, Gardening, Dance

Introduction: My name is Amb. Frankie Simonis, I am a hilarious, enchanting, energetic, cooperative, innocent, cute, joyous person who loves writing and wants to share my knowledge and understanding with you.