Operational Challenges and Solutions

Operational Challenges and Solutions

Pepper rotation presents unique challenges since changing the pepper invalidates all existing password hashes. Unlike salt rotation, which only affects new passwords, pepper changes require strategies to maintain service availability. Common approaches include dual-pepper periods where both old and new peppers are tried, gradual migration during user authentication, and batch re-hashing using reversible encryption of the original hashes.

class PepperMigrationSystem:
    """Handle pepper rotation without service disruption"""
    
    def __init__(self):
        self.old_pepper = secrets.token_bytes(32)
        self.new_pepper = secrets.token_bytes(32)
        self.migration_active = True
        self.ph = PasswordHasher()
    
    def hash_password_during_migration(self, password):
        """Hash new passwords with new pepper"""
        peppered = hmac.new(
            self.new_pepper,
            password.encode(),
            hashlib.sha256
        ).digest()
        
        hash_value = self.ph.hash(peppered.hex())
        return f"v2${hash_value}"
    
    def verify_and_migrate(self, password, stored_hash):
        """Verify password and migrate if needed"""
        
        if stored_hash.startswith("v2$"):
            # Already migrated
            _, hash_value = stored_hash.split("$", 1)
            peppered = hmac.new(
                self.new_pepper,
                password.encode(),
                hashlib.sha256
            ).digest()
            
            try:
                self.ph.verify(hash_value, peppered.hex())
                return True, None
            except:
                return False, None
                
        elif stored_hash.startswith("v1$"):
            # Old pepper
            _, hash_value = stored_hash.split("$", 1)
            peppered = hmac.new(
                self.old_pepper,
                password.encode(),
                hashlib.sha256
            ).digest()
            
            try:
                self.ph.verify(hash_value, peppered.hex())
                # Successful - migrate to new pepper
                new_hash = self.hash_password_during_migration(password)
                return True, new_hash
            except:
                return False, None
        
        else:
            # Legacy format (pre-pepper)
            try:
                self.ph.verify(stored_hash, password)
                # Migrate to peppered version
                new_hash = self.hash_password_during_migration(password)
                return True, new_hash
            except:
                return False, None
    
    def demonstrate_migration(self):
        """Show migration process"""
        
        print("Pepper Migration Demonstration:\n")
        
        # Simulate existing users
        users = {
            'alice': f"v1${self.ph.hash(hmac.new(self.old_pepper, b'alice123', hashlib.sha256).digest().hex())}",
            'bob': f"v1${self.ph.hash(hmac.new(self.old_pepper, b'bob456', hashlib.sha256).digest().hex())}",
            'charlie': self.ph.hash('charlie789')  # Legacy, no pepper
        }
        
        print("Initial state:")
        for user, hash_val in users.items():
            print(f"  {user}: {hash_val[:30]}...")
        
        print("\nLogin attempts during migration:")
        
        # Simulate logins
        login_attempts = [
            ('alice', 'alice123'),
            ('bob', 'bob456'),
            ('charlie', 'charlie789'),
            ('alice', 'alice123')  # Second login
        ]
        
        for username, password in login_attempts:
            valid, new_hash = self.verify_and_migrate(password, users[username])
            
            if valid:
                print(f"  {username}: Login successful", end="")
                if new_hash:
                    users[username] = new_hash
                    print(" - Migrated to new pepper!")
                else:
                    print(" - Already migrated")
            else:
                print(f"  {username}: Login failed")
        
        print("\nFinal state:")
        for user, hash_val in users.items():
            print(f"  {user}: {hash_val[:30]}...")

# Run migration demo
migration = PepperMigrationSystem()
migration.demonstrate_migration()

Backup and disaster recovery require special consideration with peppered passwords. Losing the pepper key makes all passwords unverifiable, effectively locking out all users. Implement secure pepper backup strategies including encrypted offline copies, split key schemes where multiple parties hold key fragments, and regular recovery testing. Document recovery procedures thoroughly, as pepper loss represents a catastrophic failure.