The improper storage of passwords poses a significant security risk to software applications. This vulnerability arises when passwords are stored in plaintext or with a fast hashing algorithm. To exploit this vulnerability, an attacker typically requires access to the stored passwords.

## Why is this an issue?

Attackers who would get access to the stored passwords could reuse them without further attacks or with little additional effort.
Obtaining the plaintext passwords, they could then gain unauthorized access to user accounts, potentially leading to various malicious activities.

### What is the potential impact?

Plaintext or weakly hashed password storage poses a significant security risk to software applications.

#### Unauthorized Access

When passwords are stored in plaintext or with weak hashing algorithms, an attacker who gains access to the password database can easily retrieve and use the passwords to gain unauthorized access to user accounts. This can lead to various malicious activities, such as unauthorized data access, identity theft, or even financial fraud.

#### Credential Reuse

Many users tend to reuse passwords across multiple platforms. If an attacker obtains plaintext or weakly hashed passwords, they can potentially use these credentials to gain unauthorized access to other accounts held by the same user. This can have far-reaching consequences, as sensitive personal information or critical systems may be compromised.

#### Regulatory Compliance

Many industries and jurisdictions have specific regulations and standards to protect user data and ensure its confidentiality. Storing passwords in plaintext or with weak hashing algorithms can lead to non-compliance with these regulations, potentially resulting in legal consequences, financial penalties, and damage to the reputation of the software application and its developers.

## How to fix it in Spring

### Code examples

#### Noncompliant code example

The following code is vulnerable because it uses a legacy digest-based password encoding that is not considered secure.

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
      auth.jdbcAuthentication()
        .dataSource(dataSource)
        .usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
        .passwordEncoder(new StandardPasswordEncoder()); // Noncompliant
    }

#### Compliant solution

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
      auth.jdbcAuthentication()
        .dataSource(dataSource)
        .usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
        .passwordEncoder(new BCryptPasswordEncoder());
    }

### How does this work?

#### Use secure password hashing algorithms

In general, you should rely on an algorithm that has no known security vulnerabilities. The MD5 and SHA-1 algorithms should not be used.

Some algorithms, such as the SHA family functions, are considered strong for some use cases, but are too fast in computation and therefore vulnerable to brute force attacks, especially with bruteforce-attack-oriented hardware.

To protect passwords, it is therefore important to choose modern, slow password-hashing algorithms. The following algorithms are, in order of strength, the most secure password hashing algorithms to date:

1. Argon2
2. scrypt
3. bcrypt
4. PBKDF2

Argon2 should be the best choice, and others should be used when the previous one is not available. For systems that must use FIPS-140-certified algorithms, PBKDF2 should be used.

Whenever possible, choose the strongest algorithm available. If the algorithm currently used by your system should be upgraded, OWASP documents possible upgrade methods here: Upgrading Legacy Hashes.

In the previous example, the `BCryptPasswordEncoder` is a password hashing function in Java that is designed to be secure and resistant to various types of attacks, including brute-force and rainbow table attacks. It is slow, adaptative, and automatically implements a salt.

#### Never store passwords in plaintext

A user password should never be stored in plaintext. Instead, a hash should be produced from it using a secure algorithm. When dealing with password storage security, best practices recommend relying on a slow hashing algorithm, that will make brute force attacks more difficult. Using a hashing function with adaptable computation and memory complexity also is recommended to be able to increase the security level with time.

Adding a salt to the digest computation is also recommended to prevent pre-computed table attacks (see rule {rule:java:S2053}).

### Pitfalls

#### Pre-hashing passwords

As bcrypt has a maximum length input length of 72 bytes for most implementations, some developers may be tempted to pre-hash the password with a stronger algorithm before hashing it with bcrypt.

Pre-hashing passwords with bcrypt is not recommended as it can lead to a specific range of issues. Using a strong salt and a high number of rounds is enough to protect the password.

More information about this can be found here: Pre-hashing Passwords with Bcrypt.

## Resources

### Documentation

- Spring Framework Security Documentation - Class BCryptPasswordEncoder
- OWASP CheatSheet - Password Storage Cheat Sheet

### Standards

- OWASP - Top 10 2021 Category A2 - Cryptographic Failures
- OWASP - Top 10 2021 Category A4 - Insecure Design
- OWASP - Top 10 2017 Category A3 - Sensitive Data Exposure
- CWE - CWE-256 - Plaintext Storage of a Password
- CWE - CWE-916 - Use of Password Hash With Insufficient Computational Effort
- STIG Viewer - Application Security and Development: V-222542 - The application must only store cryptographic representations of passwords.