Pepper Storage and Management

Pepper Storage and Management

Secure pepper storage represents the most critical implementation challenge. Unlike salts stored in databases, peppers require protection from database-level attackers. Common storage locations include application configuration files, environment variables, hardware security modules (HSMs), key management services, and separate authentication servers. Each option offers different security guarantees and operational complexity.

import os
import json
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend

class PepperStorageStrategies:
    """Demonstrate various pepper storage approaches"""
    
    @staticmethod
    def environment_variable_storage():
        """Store pepper in environment variable"""
        
        # Generate pepper if not exists
        if 'APP_PEPPER_KEY' not in os.environ:
            pepper = secrets.token_hex(32)
            print(f"export APP_PEPPER_KEY={pepper}")
            return None
        
        # Retrieve pepper
        pepper = bytes.fromhex(os.environ['APP_PEPPER_KEY'])
        print(f"Pepper loaded from environment: {len(pepper)} bytes")
        return pepper
    
    @staticmethod
    def encrypted_file_storage(password):
        """Store pepper in encrypted file"""
        
        # Derive key from password
        kdf = PBKDF2(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'app-specific-salt',
            iterations=100000,
            backend=default_backend()
        )
        key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
        fernet = Fernet(key)
        
        pepper_file = 'pepper.enc'
        
        try:
            # Try to load existing pepper
            with open(pepper_file, 'rb') as f:
                encrypted = f.read()
                pepper = fernet.decrypt(encrypted)
                print(f"Pepper loaded from encrypted file")
                return pepper
                
        except FileNotFoundError:
            # Generate and save new pepper
            pepper = secrets.token_bytes(32)
            encrypted = fernet.encrypt(pepper)
            
            with open(pepper_file, 'wb') as f:
                f.write(encrypted)
            
            print(f"New pepper generated and saved")
            return pepper
    
    @staticmethod
    def key_rotation_demo():
        """Demonstrate pepper rotation strategy"""
        
        class PepperRotation:
            def __init__(self):
                self.peppers = {
                    'v1': secrets.token_bytes(32),
                    'v2': secrets.token_bytes(32),
                    'current': 'v2'
                }
            
            def hash_with_current(self, password):
                """Hash with current pepper"""
                current_pepper = self.peppers[self.peppers['current']]
                peppered = hmac.new(
                    current_pepper,
                    password.encode(),
                    hashlib.sha256
                ).hexdigest()
                
                # Include version in hash
                return f"{self.peppers['current']}${peppered}"
            
            def verify_with_rotation(self, password, stored_hash):
                """Verify supporting old peppers"""
                version, hash_value = stored_hash.split('$', 1)
                
                if version not in self.peppers:
                    return False
                
                pepper = self.peppers[version]
                peppered = hmac.new(
                    pepper,
                    password.encode(),
                    hashlib.sha256
                ).hexdigest()
                
                return peppered == hash_value
        
        rotation = PepperRotation()
        
        # Hash with v1 (old)
        rotation.peppers['current'] = 'v1'
        old_hash = rotation.hash_with_current("password123")
        
        # Rotate to v2
        rotation.peppers['current'] = 'v2'
        new_hash = rotation.hash_with_current("password456")
        
        print("Pepper Rotation Demo:")
        print(f"Old hash (v1): {old_hash}")
        print(f"New hash (v2): {new_hash}")
        print(f"Old password still verifies: {rotation.verify_with_rotation('password123', old_hash)}")
        print(f"New password verifies: {rotation.verify_with_rotation('password456', new_hash)}")

# Demonstrate storage strategies
print("Pepper Storage Strategies:\n")

print("1. Environment Variable:")
PepperStorageStrategies.environment_variable_storage()

print("\n2. Encrypted File:")
PepperStorageStrategies.encrypted_file_storage("master-password")

print("\n3. Key Rotation:")
PepperStorageStrategies.key_rotation_demo()

Hardware security modules (HSMs) provide the highest security for pepper storage by ensuring keys never exist in plaintext outside secure hardware. Cloud providers offer managed HSM services that integrate with applications through APIs. While HSMs increase complexity and cost, they provide tamper-resistant storage and cryptographic operations, making them ideal for high-security applications.