Common Backend Vulnerabilities
Authentication & Authorization
Spring Security JWT authentication
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>
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:
Sorted from the worse to the best
Luckily, we have a great library to securely hash passwords - 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.
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.
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:
Spring Security makes using bcrypt incredibly simple.
Make sure your pom.xml includes the security starter.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
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();
}
}
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>
The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with ❤️ by the HackYourFuture community · Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.