Password storage with weak hashing

Password hashing with insufficient computational cost

Description

Plaintext storage exposes passwords directly when the storage is compromised. Fast hashes such as MD5, SHA-1, or plain SHA-256 let an attacker test many password candidates quickly against stolen hashes.

Potential impact

Unauthorized access

Recovered passwords can let an attacker access user accounts, disclose data, impersonate users, or commit fraud.

Credential reuse

If a user reuses a password, its disclosure can also compromise accounts on other services.

Legal and regulatory requirements

Inadequate password protection can violate applicable data-protection requirements and damage trust in the service.

Remediation

  1. Use a password-hashing scheme. Never store plaintext passwords. Prefer Argon2id for new systems. Configure adequate work for existing bcrypt or PBKDF2 implementations and tune it to the server's performance.
  2. Use a fresh random salt for each password. This prevents precomputed results from being reused across hashes. Store the salt with the hash; it does not prevent password guessing itself.
  3. Meet any applicable FIPS-140 requirements. Use PBKDF2-HMAC-SHA256 in a validated cryptographic module, with at least 600,000 iterations as the baseline. An algorithm name alone does not certify an implementation.

Examples

Before

These excerpts use the Spring Security 5.8 JDBC configuration style. They assume a schema that returns the username, password hash, and enabled status in the required order.

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

After

java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
  auth.jdbcAuthentication()
    .dataSource(dataSource)
    .usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
    .passwordEncoder(new BCryptPasswordEncoder()); // Use bcrypt with an adjustable work factor
}

Explanation:

  • Before: Legacy StandardPasswordEncoder uses 1,024 SHA-256 iterations. Its salt does not make that work factor sufficient for password storage.
  • After: bcrypt provides an adjustable work factor to make guessing more expensive. Check password-length limits and performance. Migrate existing hashes through rehashing at login or password resets; changing the encoder does not convert stored hashes.

References