Week 10

Security Mindset

Threat Modeling Basics

Common Backend Vulnerabilities

Handling Sensitive Data

Hashing vs. Encryption

How to Store Passwords

Authentication & Authorization

Spring Security

Spring Security JWT authentication

Practice

Assignment

Backend Track

Why Password Storage Matters

As developers, it is our responsibility to protect our users’ private data. Passwords are highly sensitive data and should be stored securely in the database.

If we do not secure our passwords in the database and our database gets stolen or leaked, all our passwords can easily be read! This is very bad for our application because with all passwords exposed, anyone can login as any user.

Leaked passwords can lead to even more problems: Many users reuse the same passwords for their email, banking app, social media accounts and more. Hackers often check stolen passwords and try to log in to many different websites to check if the user reused the password. This practice is one of the most popular methods of hacking into social media and email accounts.

To protect yourself, do not reuse passwords. You can enter your email in haveibeenpwned.com to check if your email was part of a security breach in the past. Spoiler alert: the answer is most likely yes. But don't feel bad or panic, those data breaches are very common, and have happened to the largest websites like LinkedIn and Adobe. It is likely that you used one of those websites. Just make sure that you don't use the same password on every site and change the password on the sites that have had a breach

<aside> ⌨️

Hands on: Use HaveIBeenPawned to check if your password was leaked in the past.

</aside>

How NOT to store passwords

A must-watch video by Tom Scott explaining what you should never do when handling user passwords.

https://www.youtube.com/watch?v=8ZtInClXe1Q

A few key takeouts from the video:

A summary of the methods mentioned in the video

Sorted from the worse to the best

  1. ❌ Save it as-is in plain-text.
  2. ❌ Use encryption.
  3. ❌ Use hashing.
  4. ✅  Use a slow hash+Salt.

Luckily, we have a great library to securely hash passwords - bcrypt

Secure Password Storage with bcrypt

We know now we must hash passwords, not encrypt them, the next question is: how do we hash them securely?

You will think let’s just use a standard hashing algorithm like SHA-256 to hash the password and store it in the database. It's one-way, so it's secure, right? Well, not so fast. There are so many lists nowadays of the 10 million most common passwords in the internet. Attackers can calculate the SHA-256 hash for all of them and stored them in a giant lookup table “called a rainbow table”. Then they take the hashes from the compromised database, look them up in their table, and instantly find the original password for thousands of users.

Simple Hashing is Not Enough

A simple hash is deterministic: the same input always produces the same output. hash("password123") will always result in the same hash value. This is what allows attackers to use pre-computed rainbow tables to crack passwords in seconds. So what we can do? Salt and Slowing Down

To defeat rainbow tables, we introduce randomness.

  1. Salting: A salt is a random string of data that is unique to each user. We append this salt to the password before hashing it.
  2. Slowing Down: Attackers can still try to crack one password at a time (a "brute-force" attack). To make this impractical, we should use a hashing algorithm that is intentionally slow. If it takes a fraction of a second to check one password, trying billions of combinations becomes impossibly expensive for the attacker.

The Standard - bcrypt**:**

Fortunately, we don't have to build this ourselves. We use algorithms designed for this purpose. The industry standard is bcrypt.

Why bcrypt is the right tool:

Practical Implementation in Spring Boot

Spring Security makes using bcrypt incredibly simple.

Step 1: Add the Spring Security Dependency

Make sure your pom.xml includes the security starter.xml

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

Step 2: Create a PasswordEncoder Bean

In a configuration class (e.g., SecurityConfig.java), you expose a BCryptPasswordEncoder bean. This tells Spring to use bcrypt for password operations.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        // Use the BCrypt hashing algorithm
        return new BCryptPasswordEncoder();
    }
}

Step 3: Use the Encoder in Your Service

Now you can inject (@Autowired) the PasswordEncoder into your user service to handle registration and login.

Hashing a new password (User Registration):

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public User registerNewUser(UserRegistrationDto registrationDto) {
        User newUser = new User();
        newUser.setUsername(registrationDto.getUsername());

        // NEVER store the plain text password. Always encode it!
        String encodedPassword = passwordEncoder.encode(registrationDto.getPassword());
        newUser.setPassword(encodedPassword);

        return userRepository.save(newUser);
    }
}

The following code will hash the user password. It looks something like this:

$2b$12$vfQ6eiT2aU2d.Im0LVBm6.dG3r1IYJg2FxmsnNBsuUHFPHTYYZ7rO

It is now safe to store it in the database.

<aside> ⌨️

Hands on: Use the encode to hash on the same input twice (e.g “password”). Compare the encoded results - are they identical?

</aside>

Verifying a password (User Login):

public boolean checkUserCredentials(String rawPassword, String encodedPasswordFromDb) {
        // The .matches() method handles both salting and hashing.
        // It will re-hash the raw password with the salt from the encoded password
        // and compare the results.
        return passwordEncoder.matches(rawPassword, encodedPasswordFromDb);
    }

<aside> ❗

As a developer, you only need to call encode to store a password and matches to check it. You never, ever store a plain text password, not even for a moment.

</aside>

<aside> ⌨️

Hands on: Use the matches method to find the original password for the following hash

$2a$10$UL/kfYXivgX9gyBwcWC.fuPRMONfRMB4ebGBov2suqivgsmAhtF5m

It should be either HackYourFuture0509 , HackYourFuture1337 or HackYourFuture9087

</aside>

Extra resources

Reading material

Videos


The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

CC BY-NC-SA 4.0 Icons

Built with ❤️ by the HackYourFuture community · Thank you, contributors

Found a mistake or have a suggestion? Let us know in the feedback form.