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.

User A (Alice)
Password
Salt (Random)
x8L2p9
Stored Hash
...
User B (Bob)
Password
Salt (Random)
m4K1sZ
Stored Hash
...
Even though passwords are identical, the hashes are completely different.

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.

Go
Java
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
    }
}