Insecure service configuration - Salt
Need
Use of a unique random salt for every password hash
Context
• Usage of Rust 1.75+ for building application services
• Usage of the bcrypt crate for password hashing
Description
1. Non compliant code
use bcrypt::{hash_with_salt, BcryptResult, DEFAULT_COST};
fn hash_password(password: &str) -> BcryptResult<String> {
// Every user gets the same salt, so equal passwords produce equal hashes
let parts = hash_with_salt(password, DEFAULT_COST, *b"fixedsalt1234567")?;
Ok(parts.to_string())
}The `hash_password` function below calls `bcrypt::hash_with_salt` with the fixed 16-byte salt `b"fixedsalt1234567"`. A salt exists to make each hash unique, so identical passwords produce different hashes and an attacker has to attack each one separately. A salt that is the same for every user removes that protection: all users who chose the same password get the same hash, which reveals them at a glance, and an attacker can precompute the hashes of common passwords once, with that salt, and match the whole database against them. Because the salt is in the source code, it is also available to anyone who reads the repository or the binary.
2. Steps
• Use `bcrypt::hash` or Argon2 `SaltString::generate(&mut OsRng)` so that every password gets its own random salt.
• Remove salts written as literals, constants or shared configuration values from calls such as `bcrypt::hash_with_salt`.
• Store the full bcrypt or PHC string, which already contains the salt, and verify with `bcrypt::verify` or `Argon2::verify_password`.
• Keep the bcrypt cost at 10 or higher; `bcrypt::DEFAULT_COST` is 12.
• Re-hash the existing passwords with random salts at the next successful login of each user.
3. Secure code example
use bcrypt::{hash, verify, BcryptResult, DEFAULT_COST};
fn hash_password(password: &str) -> BcryptResult<String> {
// bcrypt::hash draws a fresh random salt for every call
hash(password, DEFAULT_COST)
}
fn verify_password(password: &str, stored_hash: &str) -> BcryptResult<bool> {...The corrected function calls `bcrypt::hash`, which generates a new 16-byte random salt from the operating system for every call. Two users with the same password now get unrelated hashes, and precomputed tables no longer apply. The salt does not need to be stored separately: the bcrypt string that `hash` returns has the form `$2b$<cost>$<salt><hash>`, so the salt and the cost travel with the hash. `verify_password` passes the stored string to `bcrypt::verify`, which reads both from it. When an API requires an explicit salt, such as `hash_with_salt` or Argon2 `SaltString`, it must be generated per password with `OsRng`, never taken from a constant or a configuration value.
References
• 338. Insecure service configuration - Salt