Authentication
Authentication is the process of verifying identity. The most common method is the password. But how do you store a password securely? Rule #1: NEVER store passwords in plain text.
If you store passwords in a database like SELECT * FROM users, a single SQL Injection leak compromises everyone. Instead, we store a Hash.
1. The Physics of Hashing
A Hash Function (like SHA-256) is a one-way mathematical trapdoor.
Hash("password123") → ef92b7...
The Attack: Rainbow Tables
Hackers pre-compute the hashes for billions of common passwords. If your database contains ef92b7..., they instantly know the password is “password123”.
The Defense: Salting
A Salt is a random string added to the password before hashing.
Hash("password123" + "RandomSaltX") → TotallyDifferentHash
This defeats pre-computed tables because the attacker would need a new table for every unique salt.
The Defense: Peppering
A Pepper is a secret key stored outside the database (e.g., in an environment variable).
Hash("password123" + Salt + Pepper)
2. Interactive: Salted Hash Visualizer
See how two users with the same password get different hashes due to unique salts.
3. Work Factors: Why Speed is Bad
You might think: “I want a fast hash function!” Wrong. If a hash takes 1 nanosecond, a hacker can guess 1 billion passwords per second. You want a hash that takes 100 milliseconds (slow for CPU, fast enough for human login). This is the Work Factor.
- Bad: MD5, SHA-1, SHA-256 (Too fast).
- Good: Argon2, Bcrypt, Scrypt (Memory-hard and CPU-hard).
4. Code Example: Secure Hashing
Using industry-standard libraries.
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := []byte("secret123")
// Hashing the password.
// Cost 10 means 2^10 iterations.
hash, err := bcrypt.GenerateFromPassword(password, 10)
if err != nil {
panic(err)
}
fmt.Printf("Stored Hash: %s\n", hash)
// Verifying the password
err = bcrypt.CompareHashAndPassword(hash, password)
if err == nil {
fmt.Println("Success: Password matches.")
} else {
fmt.Println("Failure: Access Denied.")
}
}
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Base64;
public class PasswordUtil {
// PBKDF2 (Password-Based Key Derivation Function 2)
// Standard in Java (Argon2 requires external libs)
public static String hashPassword(String password, byte[] salt) throws Exception {
int iterations = 65536;
int keyLength = 256;
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, keyLength);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
}
public static void main(String[] args) throws Exception {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
String hash = hashPassword("secret123", salt);
System.out.println("Hash: " + hash);
// Store 'hash' AND 'salt' in DB
}
}