dwgebler/encrypted-fields-bundle
Install the Package
Add the Flex recipe endpoint to composer.json (optional but recommended):
"extra": {
"symfony": {
"endpoint": ["https://api.github.com/repos/dwgebler/flex-recipes/contents/index.json"]
}
}
Install via Composer:
composer require dwgebler/encrypted-fields-bundle
Generate Master Key Create a secure master key file (32-byte hex for AES-256-GCM):
php -r "file_put_contents('master.key', bin2hex(random_bytes(32)));"
Store the path in .env:
ENCRYPTED_FIELDS_KEY=file:///path/to/master.key
Configure the Bundle
Add to config/packages/gebler_encrypted_fields.yaml:
encrypted_fields:
master_key: '%env(trim:file:ENCRYPTED_FIELDS_KEY)%'
cipher: 'aes-256-gcm'
Run Migrations
Generate and execute the encryption_key table migration:
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Annotate an Entity
Add the #[EncryptedField] attribute to a sensitive field in a Doctrine entity:
use Gebler\EncryptedFieldsBundle\Attribute\EncryptedField;
#[ORM\Entity]
class User {
#[EncryptedField]
private string $ssn;
#[EncryptedField(elements: ['credit_card'])]
private array $paymentDetails;
}
Test Encryption Persist and retrieve an entity to verify automatic encryption/decryption:
$user = new User();
$user->ssn = '123-45-6789';
$entityManager->persist($user);
$entityManager->flush();
$fetchedUser = $entityManager->find(User::class, $user->getId());
// $fetchedUser->ssn is automatically decrypted
#[EncryptedField] on Doctrine entity properties.#[EncryptedField]
private string $apiKey;
#[EncryptedField(elements: ['token'])]
private array $authTokens;
persist() or flush(), the bundle encrypts the field with a per-record key.find() or getReference(), it decrypts the field automatically.useMasterKey: true for fields requiring the master key (e.g., audit logs):
#[EncryptedField(useMasterKey: true)]
private string $adminNotes;
encryption_key table) encrypted with the master key.#[EncryptedField(elements: ['password', 'token'])]
private array $userCredentials;
password, token) are encrypted; others remain plaintext.Pattern: Rotate the master key without decrypting all data:
php bin/console gebler:encryption:rotate-key --generate-new-key
Workflow:
Legacy Data Handling:
php bin/console gebler:encryption:rotate-key --database-key-file=/path/to/old.key --generate-new-key
#[EncryptedField(key: 'custom_key_for_this_field')]
private string $legacyData;
Dependency Injection:
$this->container->get('gebler_encrypted_fields.encrypter');
use Gebler\EncryptedFieldsBundle\Encrypter\EncrypterInterface;
public function __construct(private EncrypterInterface $encrypter) {}
Event Listeners:
use Doctrine\ORM\Events;
use Gebler\EncryptedFieldsBundle\EventListener\EncryptedFieldListener;
// In services.yaml
Doctrine\ORM\EntityManager:
tags: ['doctrine.event_listener']
calls:
- [addEventListener, [Events::prePersist, '@gebler_encrypted_fields.listener']]
Console Commands:
rotate-key command for custom logic:
use Gebler\EncryptedFieldsBundle\Command\RotateKeyCommand;
// Override in a custom command class.
Service Provider:
AppServiceProvider:
public function register()
{
$this->app->register(\Gebler\EncryptedFieldsBundle\EncryptedFieldsBundle::class);
}
Configuration:
$this->app->bind('gebler_encrypted_fields.master_key', function () {
return env('ENCRYPTED_FIELDS_KEY');
});
Artisan Commands:
Artisan:
Artisan::command('gebler:encryption:rotate-key', function () {
// Custom logic or proxy to Symfony command.
});
Caching Decrypted Values:
$cache = $this->container->get('cache.app');
$cachedValue = $cache->get("user_{$user->id}_ssn", function () use ($user) {
return $user->ssn; // Automatically decrypted
});
Batch Processing:
$encrypter = $this->container->get('gebler_encrypted_fields.encrypter');
$plaintext = $encrypter->decrypt($encryptedData);
Indexing:
WHERE clauses (performance impact). Use computed columns or triggers if needed.OpenSSL Requirement:
Class 'OpenSSL' not found.sudo apt-get install php-openssl # Debian/Ubuntu
sudo dnf install php-openssl # RHEL/CentOS
php -m | grep openssl.Master Key Exposure:
config/packages/gebler_encrypted_fields.yaml).%env() or a secrets manager:
encrypted_fields:
master_key: '%env(ENCRYPTED_FIELDS_KEY)%'
Proxy Objects:
Key Rotation Downtime:
rotate-key command locks the encryption_key table during re-encryption.Large Binary Fields:
BLOB/TEXT fields may hit memory limits.pgcrypto).Multi-Environment Keys:
--database-key-file during migrations.How can I help you explore Laravel packages today?