caeligo/field-encryption-bundle
This document covers the key rotation process for safely replacing encryption keys.
Key rotation is the process of replacing your encryption key while maintaining access to existing encrypted data. This is necessary when:
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY)%'
key_version: 1
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY_V2)%' # Current key
key_version: 2
previous_keys:
- version: 1
key: '%env(FIELD_ENCRYPTION_KEY_V1)%' # Previous key
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY_V2)%'
key_version: 2
# previous_keys removed after confirming all data is migrated
php bin/console field-encryption:generate-key --env-format
Save the output to your environment:
# .env.local
FIELD_ENCRYPTION_KEY_V1=your_current_key_here
FIELD_ENCRYPTION_KEY_V2=newly_generated_key_here
# config/packages/field_encryption.yaml
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY_V2)%'
key_version: 2
previous_keys:
- version: 1
key: '%env(FIELD_ENCRYPTION_KEY_V1)%'
Deploy the updated configuration. At this point:
# Recommended: Use wizard mode
php bin/console field-encryption:rotate-keys --wizard
# Or: Dry run first
php bin/console field-encryption:rotate-keys --dry-run
# Then: Execute rotation
php bin/console field-encryption:rotate-keys
# Check for any remaining old-version data
php bin/console field-encryption:rotate-keys --dry-run
# Should report: "0 entities need key rotation"
After confirming all data is migrated:
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY_V2)%'
key_version: 2
# previous_keys removed
Remove FIELD_ENCRYPTION_KEY_V1 from your environment.
Rotation progress is saved to:
var/field_encryption_rotation_progress.json
Example content:
{
"started_at": "2024-01-15T10:30:00+00:00",
"entities": {
"App\\Entity\\User": {
"total": 1000,
"processed": 750,
"last_id": "01HQ1234567890ABCDEFGH"
},
"App\\Entity\\Document": {
"total": 500,
"processed": 500,
"completed_at": "2024-01-15T10:45:00+00:00"
}
}
}
If rotation is interrupted (server restart, timeout, etc.):
php bin/console field-encryption:rotate-keys --continue
The command will resume from the last processed entity.
# Test in staging/development first
php bin/console field-encryption:rotate-keys --dry-run
# Database backup before rotation
mysqldump -u user -p database > backup_before_rotation.sql
Key rotation is I/O intensive. Schedule during maintenance windows.
Watch the progress output or check the progress file:
watch cat var/field_encryption_rotation_progress.json
Don't remove previous_keys until you've verified:
For large datasets, adjust batch size:
# Smaller batches = less memory, more DB queries
php bin/console field-encryption:rotate-keys --batch-size=25
# Larger batches = more memory, fewer DB queries
php bin/console field-encryption:rotate-keys --batch-size=200
For environments with multiple key versions:
field_encryption:
encryption_key: '%env(FIELD_ENCRYPTION_KEY_V3)%'
key_version: 3
previous_keys:
- version: 1
key: '%env(FIELD_ENCRYPTION_KEY_V1)%'
- version: 2
key: '%env(FIELD_ENCRYPTION_KEY_V2)%'
The bundle will automatically use the correct key based on the version stored in each encrypted payload.
Currently, string fields don't include key version in the payload. Re-encryption happens when data is loaded and saved.
Binary fields include key version in the payload header:
[magic: "CEFF"]
[format_version: 1]
[key_version: N] <-- Key version stored here
[flags: ...]
...
This allows:
If a key is compromised:
Generate new key immediately:
php bin/console field-encryption:generate-key --append-to-env
Update configuration with new key and increment version
Deploy immediately
Run rotation with priority:
php bin/console field-encryption:rotate-keys --batch-size=200
Remove compromised key from all environments
The data was encrypted with a key version not in your configuration.
Solution: Add the missing key to previous_keys.
Large datasets may take hours.
Solutions:
Solution: Decrease batch size:
php bin/console field-encryption:rotate-keys --batch-size=10
Solution: Resume with:
php bin/console field-encryption:rotate-keys --continue
How can I help you explore Laravel packages today?