When we talk about password security, the conversation usually goes straight to hashing algorithms — things like SHA-256, bcrypt, or Argon2.
But there are two lesser-known players that can make or break your defenses: salts and pepper.
Think of them as seasoning for your password hashes — not for flavor, but for security.
Why Password Hashing Alone Isn’t Enough
Hashing is like putting your password through a one-way blender — you can’t (easily) get the original password back.
But if attackers get your hashed password database, they can still use rainbow tables or brute-force attacks to figure out the original passwords.
That’s where salts and pepper come in.
They make every hash unique and harder to crack — even if someone has your database.
Salts vs. Pepper: What’s the Difference?
Salts
- A random value added to each password before hashing.
- Stored alongside the hash in the database.
- Makes it impossible for attackers to use precomputed hash tables.
- Every user gets a unique salt.
Pepper
- A secret value added to the password before hashing.
- Not stored in the database — kept separately (e.g., in environment variables or secure key vaults).
- Even if the attacker steals your database, they can’t crack hashes without the pepper.
In short:
- Salt is public but unique per password
- Pepper is secret and the same for all passwords (or sometimes per user, but still hidden).
Kotlin Example: Salting and Peppering Passwords
Let’s see this in Kotlin. We’ll use the MessageDigest API for hashing (for simplicity), though in real production you should use stronger libraries like BCrypt or Argon2.
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.Base64
object PasswordHasher {
// Generate a random salt for each password
fun generateSalt(length: Int = 16): String {
val random = SecureRandom()
val salt = ByteArray(length)
random.nextBytes(salt)
return Base64.getEncoder().encodeToString(salt)
}
// Your secret pepper - should be stored securely (e.g., env variable)
private const val PEPPER = "SuperSecretPepperValue123!"
// Hash with salt + pepper
fun hashPassword(password: String, salt: String): String {
val saltedPepperedPassword = password + salt + PEPPER
val digest = MessageDigest.getInstance("SHA-256")
val hashBytes = digest.digest(saltedPepperedPassword.toByteArray(Charsets.UTF_8))
return Base64.getEncoder().encodeToString(hashBytes)
}
// Verify password
fun verifyPassword(inputPassword: String, storedSalt: String, storedHash: String): Boolean {
val inputHash = hashPassword(inputPassword, storedSalt)
return inputHash == storedHash
}
}
fun main() {
val password = "MySecurePassword!"
// 1. Generate salt
val salt = PasswordHasher.generateSalt()
// 2. Hash password with salt + pepper
val hashedPassword = PasswordHasher.hashPassword(password, salt)
println("Salt: $salt")
println("Hash: $hashedPassword")
// 3. Verify
val isMatch = PasswordHasher.verifyPassword("MySecurePassword!", salt, hashedPassword)
println("Password match: $isMatch")
}
Salt Generation
- We create a random salt using
SecureRandom
. - This ensures no two hashes are the same, even if passwords are identical.
Pepper Usage
- The pepper is stored outside the database, often in environment variables or secure vaults.
- It’s the “secret ingredient” that attackers won’t see if they only have the database.
Hashing
- We combine the password + salt + pepper before hashing with SHA-256.
- In production, replace SHA-256 with bcrypt or Argon2 for better resistance against brute force.
Verification
- When a user logs in, we retrieve the stored salt, hash the provided password with the same pepper, and compare the results.
Best Practices for Salts and Pepper
- Always use a unique salt for each password. Never reuse salts.
- Store salts with the hash in the database.
- Keep pepper secret — in an environment variable, key management system, or hardware security module.
- Use a slow, memory-hard hashing algorithm like bcrypt, scrypt, or Argon2.
- Rotate peppers periodically for maximum security.
- Never hard-code pepper in your source code for production.
Why Salts and Pepper Matters
Attackers thrive on shortcuts. Salts remove the shortcut of using rainbow tables. Pepper blocks attackers even if they have your entire password database.
Together, they make your password security significantly harder to break.
Conclusion
When it comes to security, the little details — like salts and pepper — make a big difference.
Hashing without them is like locking your front door but leaving the window wide open.
So next time you store a password, make sure it’s seasoned with both.