Insecure service configuration - Salt
Need
Use of a unique random salt and a strong key derivation for every password
Context
• Usage of Kotlin 1.9+ on the JVM for building application services
• Usage of javax.crypto for password-based key derivation
Description
1. Non compliant code
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
private val SALT = "WeakStaticSalt123".toByteArray()
fun hashPassword(password: String): ByteArray {
// Same salt for every user, and too few iterations
val spec = PBEKeySpec(password.toCharArray(), SALT, 10_000, 256)...The `hashPassword` function below derives a password hash with PBKDF2 and the constant salt `"WeakStaticSalt123"`. A salt exists to make each hash unique, so an attacker has to attack each password separately. With one salt for every user, all users who chose the same password get the same hash, which reveals them at a glance, and an attacker can compute the hashes of common passwords once, with that salt, and match the whole database against them. The function also uses `PBKDF2WithHmacSHA1` with only 10,000 iterations, far below current recommendations, so each guess is cheap. Salts created with `PBEParameterSpec` from constant values have the same problem.
2. Steps
• Generate a new salt of at least 16 bytes with `SecureRandom` for every password, and store it with the hash.
• Remove constant salts passed to `PBEKeySpec` and `PBEParameterSpec`.
• Use `PBKDF2WithHmacSHA256` with at least 600,000 iterations, or Argon2id, instead of `PBKDF2WithHmacSHA1` with few iterations.
• Compare hashes with `MessageDigest.isEqual` to avoid timing differences.
• Re-hash existing passwords with random salts at the next successful login of each user.
3. Secure code example
import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
private const val SALT_BYTES = 16
private const val ITERATIONS = 600_000
private const val KEY_BITS = 256...The corrected function generates a new 16-byte salt from `SecureRandom` for every password and returns it together with the hash, so it can be stored in the same record and used again at login. The derivation uses `PBKDF2WithHmacSHA256` with 600,000 iterations, which is the current OWASP recommendation for this algorithm. The `PBEKeySpec` is cleared after use so the password characters do not stay in memory longer than needed. `verify` recomputes the hash with the stored salt and compares both values with `MessageDigest.isEqual`, which takes the same time regardless of where they differ. Argon2id, available through libraries such as Bouncy Castle or Spring Security, is an even stronger alternative to PBKDF2.
References
• 338. Insecure service configuration - Salt