specshaper/encrypt-bundle
SpecShaper EncryptBundle adds transparent Doctrine field encryption to Symfony apps. Mark entity properties as encrypted and the bundle automatically handles encryption and decryption, keeping sensitive database data protected at rest with configurable keys.
First, the caveat that shapes everything else: specshaper/encrypt-bundle is a Symfony bundle built around Doctrine ORM event subscribers, not a Laravel package. There is no service provider, no config/encrypt.php, and no Eloquent integration. A Laravel developer has two realistic paths:
laravel-doctrine/orm — register the bundle's DoctrineEncryptSubscriber on the EntityManager's event manager in a service provider and you get transparent field encryption.encrypted cast ('ssn' => 'encrypted', plus encrypted:array|json|collection) that covers the same use case with zero extra dependencies. Use this bundle only if you specifically need its Doctrine attribute-driven, driver-swappable design.Assuming the Doctrine path:
composer require specshaper/encrypt-bundle — this is a low-visibility fork of mogilvie/EncryptBundle (105 stars upstream), so verify the namespace (Specshaper\… vs Mogilvie\…) and config alias before wiring anything. There is no Flex recipe; register the bundle manually in config/bundles.php.openssl rand -base64 32, store as ENCRYPT_KEY in .env.specshaper_encrypt or the retained upstream mogilvie_encrypt — check DependencyInjection/Configuration.php):# config/packages/specshaper_encrypt.yaml
specshaper_encrypt:
key: '%env(ENCRYPT_KEY)%'
cipher: 'aes-256-gcm'
encryptor_class: Specshaper\EncryptBundle\Encryptors\OpenSslEncryptor
use Specshaper\EncryptBundle\Configuration\Encrypted;
#[ORM\Column(type: 'text')]
#[Encrypted]
private string $ssn;
Where to look first: EncryptService / the encryptor interface (driver selection and output encoding), DoctrineEncryptSubscriber (the postLoad / prePersist / preUpdate hooks), and the Configuration/Encrypted attribute. First use case: PII columns — SSNs, medical notes, API tokens — where you want encryption to be invisible to repository and controller code.
postLoad and encrypts before flush, so $repo->find($id) returns plaintext while the DB holds base64 ciphertext. No repository changes required; this is the bundle's whole value proposition.EncryptServiceInterface anywhere and call encrypt() / decrypt() for queue payloads, cache entries, or files that never touch an entity.hash_hmac('sha256', strtolower($email), $indexKey) and query on that. Keep the index key separate from the encryption key.$em->refresh()/clear() between batches, decrypts with the old key and re-encrypts with the new one. Do not hold the whole table in the identity map.json_encode'd — decrypted values leak into logs and responses.text column and json_decode in a getter, rather than annotating each nested property.protected $casts = ['meta' => 'encrypted:array'], Crypt::encryptString() for payloads, and a custom cast + hash_hmac column for deterministic search.WHERE ssn = :ssn returns zero rows because every write produces different ciphertext. This fails silently — no exception, just empty results. Always use a blind-index column for filtering, sorting, or uniqueness.varchar(255) that fit the plaintext will truncate the ciphertext, and truncation manifests as a decryption failure at read time, not at write time. Use text.SELECT PARTIAL, scalar DQL, native queries, and DB::select() all return raw ciphertext. Doctrine's change tracking also compares decrypted values, so writes triggered outside the subscriber can persist plaintext.openssl_decrypt returns false and the bundle throws a generic exception; the usual cause is an ENCRYPT_KEY mismatch between environments, not a cipher change. Check the key first.latin1 or misconfigured connection can mangle stored ciphertext; verify with SHOW VARIABLES LIKE 'character_set_%' before blaming the crypto.encrypted cast, adding this bundle's #[Encrypted] attribute produces ciphertext-of-ciphertext and confusing failures. One mechanism per column.#[Encrypted] attribute won't take effect until cache clear/warmup.openssl_* call per persist/load. Bulk inserts of tens of thousands of rows degrade measurably; measure before assuming the overhead is free.mogilvie/EncryptBundle, and be prepared to maintain it yourself.How can I help you explore Laravel packages today?