DEV Community

Cover image for How I Secured Passwords in My Spring Boot Project (N1netails) Using BCrypt
Shahid Foy
Shahid Foy

Posted on

How I Secured Passwords in My Spring Boot Project (N1netails) Using BCrypt

With the rise of LLMs and the 'vibe coding' movement, many cool ideas are going from concept to code in days or even hours. I'm all for this creative energy—I've always seen programming as a tool to bring ideas to life. But one thing that concerns me is whether these quickly built apps include proper security measures—especially for protecting sensitive data like user passwords.

If you do not use the right methods for password hashing or if you use outdated hashing methods you risk leaking your customers sensitive information, like their passwords. It's best practice to use a unique password for every site, but most people end up reusing the same one—or small variations of it—because remembering dozens of passwords is nearly impossible without a password manager.

The reality is I am sure that a lot of these vibe coded websites might be littered with software vulnerabilities. That’s just how things are right now—LLMs often don’t provide solutions for the latest versions of a framework. Instead, they usually suggest code based on older versions they were trained on. And if you're relying on an LLM to generate your backend code without explicitly handling password security, it might suggest saving passwords as plain text—introducing a huge security risk.

Today I wanted to share how I implemented Bcrypt hashing on my open-source project N1netails.

What are hashing algorithms and how do they help you with protecting user passwords?

Hashing algorithms are mathematical functions that take an input (or "message") and return a fixed-size string of characters, which is typically a hash. The key properties of cryptographic hashing algorithms are:

  • Deterministic: The same input always gives the same output.
  • Fast computation: Quickly produce the hash.
  • Irreversibility: It’s computationally infeasible to reverse the hash to get the original input.
  • Collision resistance: It’s hard to find two different inputs that produce the same hash.
  • Avalanche effect: A small change in input drastically changes the hash output.

How Hashing Helps Protect User Passwords

When users create accounts, you don’t store the password directly. Instead:

  1. You hash the password using a secure algorithm.
  2. Store the hashed value in your database.
  3. During login, hash the password attempt and compare it with the stored hash. This means that even if someone steals your database, they don’t get actual passwords.

To further improve security, we use:

  • Salts: Random values added to the password before hashing to prevent precomputed attacks (rainbow tables).
  • Slow hashing: To resist brute-force attacks (e.g., bcrypt, scrypt, Argon2).

MD5 vs Bcrypt

Feature MD5 Bcrypt
Algorithm Type Fast cryptographic hash function Adaptive key derivation function
Designed For Checksums, not password storage Secure password hashing
Speed Extremely fast Intentionally slow
Salt Support No (must be added manually) Built-in
Collision Resistance Weak (many known collisions) Strong
Adjustable Cost No Yes (work factor or cost)
Secure? ❌ No ✅ Yes (as of now)

Why MD5 is Bad for Passwords

  • Fast: Makes brute-force attacks much easier.
  • No salting by default: Allows precomputed attacks (rainbow tables).
  • Broken cryptography: Collisions are trivial to generate.
  • Not designed for passwords: Originally used for checksums (e.g., file integrity).

⚠️ MD5 is considered cryptographically broken and should never be used for passwords.

How I used Bcrypt with Spring Security to hash user passwords before they are saved into the database

Hashing passwords can be tricky if you're trying to implement it from scratch. That’s why I decided to use the existing Spring Security framework and the BCryptPasswordEncoder it provides.

View the source code here:
https://github.com/n1netails/n1netails/blob/v0.2.9/n1netails-api/src/main/java/com/n1netails/n1netails/api/config/ProjectSecurityConfig.java

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

...
public class ProjectSecurityConfig {
    private final BCryptPasswordEncoder passwordEncoder;
    ...

    @Bean
    public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
        return authenticationManagerBuilder.build();
    }
}
Enter fullscreen mode Exit fullscreen mode

The method above defines a Spring @Bean that provides a custom AuthenticationManager for your application. Here’s what it’s doing under the hood:


🔍 Method Breakdown

@Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
Enter fullscreen mode Exit fullscreen mode
  • Declares a Spring bean named authenticationManagerBean.
  • Returns an instance of AuthenticationManager, which is used by Spring Security to authenticate users.
  • Takes HttpSecurity as a parameter, so it can access shared security configuration.

AuthenticationManagerBuilder authenticationManagerBuilder = 
    http.getSharedObject(AuthenticationManagerBuilder.class);
Enter fullscreen mode Exit fullscreen mode
  • Retrieves the shared AuthenticationManagerBuilder from the HttpSecurity context.
  • This builder is used to configure how authentication should work.

authenticationManagerBuilder
    .userDetailsService(userDetailsService)
    .passwordEncoder(passwordEncoder);
Enter fullscreen mode Exit fullscreen mode
  • userDetailsService(userDetailsService): Tells Spring how to load user information (e.g., from a database).
  • passwordEncoder(passwordEncoder): Tells Spring how to verify the password (e.g., using BCryptPasswordEncoder).

This configuration links your authentication mechanism to your custom logic.


return authenticationManagerBuilder.build();
Enter fullscreen mode Exit fullscreen mode
  • Finalizes the configuration and returns a fully built AuthenticationManager bean that will be used by Spring Security to authenticate login attempts.

✅ What This Achieves

  • You provide a custom way to authenticate users (via your UserDetailsService).
  • You ensure secure password handling using a specific encoder (e.g., bcrypt).
  • You expose this AuthenticationManager bean so it can be used in other parts of your app, like a custom login filter or controller.

🔒 Why It's Needed

Spring Security (since version 5.7) recommends defining your own AuthenticationManager bean if you're doing custom authentication, instead of relying on the default global one.

Without this, Spring might not know how to authenticate users, or might not use your desired password encoder.

When used with my SecurityFilterChain the AuthenticationManager bean defined above is automatically injected into the filter chain.

    @Bean
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        ...
        http.authorizeHttpRequests(
                auth -> auth
                        .requestMatchers(PUBLIC_URLS).permitAll()
                        .anyRequest().authenticated()
        );

        return http.build();
    }
Enter fullscreen mode Exit fullscreen mode

🔐 The Result

Now users no longer need to worry about their passwords being saved into the database in plain text. The result will now be hashed and will not be useful to hackers if they managed to get into the N1netails database.

You may notice that some users don’t even have passwords. That’s because I added support for passkey-based logins, which you can read more about here:
https://dev.to/shahidfoy/adding-passkey-login-to-my-open-source-project-n1netails-using-yubico-and-webauthn-passkeys-2iae

Sadly after working on my passkey implementation for some time I decided to make passkeys as a secondary login method because I currently do not have a way to validate user emails before sign up which I am planning to implement in the future.

If you're vibe-coding your next side project, I'm rooting for you! Just remember: security matters, especially when you're handling user data.

Cheers, and happy coding!

Top comments (0)