psolutions/encrypt-bundle
最新稳定版本:v5.0.0
Composer 安装命令:
composer require psolutions/encrypt-bundle
包简介
Bundle provides a service for encrypting values via attribute.
关键字:
README 文档
README
A bundle to handle encoding and decoding of parameters using OpenSSL and Doctrine lifecycle events. It's a fork of https://github.com/mogilvie/EncryptBundle
Features include:
- v1 is Symfony 6.4 and 7.0 compatible.
- Uses OpenSSL with AES-256-GCM authenticated encryption (since v2.1)
- Uses Event listeners
- Provides confidentiality AND authenticity (tamper-proof)
⚠️ IMPORTANT: Migration from Pre-v2.1 Versions
This bundle now uses AES-256-GCM instead of the old AES-256-CBC method.
If you are currently using a previous version of this bundle and have data already encrypted in your database:
MIGRATION STEPS REQUIRED
-
Export your existing encrypted data using the OLD version (v2.0.x):
# With the old version installed, decrypt everything first: php bin/console encrypt:database decrypt -
Update the bundle to v2.1+:
composer update psolutions/encrypt-bundle
-
Re-encrypt all data with the new GCM method:
php bin/console encrypt:database encrypt
WARNING: If you skip the migration step, the new version will NOT be able to decrypt data encrypted with the old CBC method. The two formats are incompatible.
About AES-256-GCM
- Authenticated Encryption: AES-GCM provides both encryption AND message authentication.
- Tamper Detection: Any modification to the encrypted data will be detected and will cause decryption to fail.
- No Padding Oracle Vulnerabilities: Unlike CBC mode, GCM is not vulnerable to padding oracle attacks.
- Format: Encrypted values are stored as
base64(IV . TAG . ciphertext) . '<ENC>'.
Features road map:
- Create a factory method to expand for different encryptors
- Create a twig function to decrypt encoded values
- Expand parameters to allow selection of encoding method
- Create CLI commands to encrypt and decrypt the entire database
- Handle DateTime data types via the bundle.
License
This bundle is under the MIT license. See the complete license in the bundle:
Resources/meta/LICENSE
About
EncryptBundle has been written for the Parolla Plugins and Parolla websites to encode users private data. The bundle is expanded in a larger gdpr-bundle.
Reporting an issue or a feature request
Issues and feature requests are tracked in the Github issue tracker.
When reporting a bug, it may be a good idea to reproduce it in a basic project built using the Symfony Standard Edition to allow developers of the bundle to reproduce the issue by simply cloning it and following some steps.
Installation
Step 1: Install from package
Open a command console, enter your project directory and execute the following command to download the latest development version of this bundle:
$ composer require psolutions/encrypt-bundle
Step 2: Enable the bundle
The receipe will create a package config file under config/packages/psolutions_encrypt.yaml.
If required, enable the bundle by adding it to the list of registered bundles
in the config/bundles.php file of your project:
<?php return [ ... PSolutions\EncryptBundle\PSolutionsEncryptBundle::class => ['all' => true], ];
Step 2: Configure the bundle
Generate a 256-bit encryption key using the command provided:
$ bin/console encrypt:genkey
Copy the key into your .env file:
###> encrypt-bundle ###
PSOLUTIONS_ENCRYPT_KEY= change_me!
###< encrypt-bundle ###
Maker will have created a packages yaml file. The key is resolved in there.
# config/packages/psolutions_encrypt.yaml psolutions_encrypt: encrypt_key: '%env(PSOLUTIONS_ENCRYPT_KEY)%' is_disabled: false # Turn this to true to disable the encryption. connections: # Optional, define the connection name(s) for the subscriber to listen to. - 'default' - 'tenant' encryptor_class: App\Encryptors\MyCustomEncryptor # Optional to override the bundle OpenSslEncryptor. annotation_classes: # Optional to override the default Attribute object. - App\Annotation\MyAttribute
The bundle uses AES-256-GCM authenticated encryption by default. You can override the encryptor class if you need a different cipher method, but it's strongly recommended to keep GCM for security.
You can disable encryption by setting the 'is_disabled' option to true. Decryption still continues if any values contain the <ENC> suffix.
If you want to define your own attribute, then this can be used to trigger encryption by adding the attribute to properties class that you want yo be encrypted.
You can pass the class name of your own encyptor service using the optional encryptorClass option.
Alternative EncryptKeyEvent
The EncryptKey can be set via a dispatched event listener, which overrides any .env or param.yml defined key. Create a listener for the EncryptKeyEvents::LOAD_KEY event and set your encryption key at that point.
Step 3: Create the entities
Add the Encrypted attribute class within the entity.
<?php ... use PSolutions\EncryptBundle\Annotations\Encrypted;
Add the attribute #[Encrypted] to the properties you want encrypted.
<?php #[Encrypted] #[Column] protected string $taxNumber; #[Column(type: string, nullable: true)] #[Encrypted] protected ?bool $isSelfEmployed; /** * Date of birth */ #[Encrypted] #[Column] protected ?String $dob;
Where encrypting a field you will need to set the column type as string.
Your getters and setters may also need to be type declared.
For example, boolean should either be return declared bool, or return a bool using a ternary method.
<?php /** * Get isSelfEmployed * * @return boolean */ public function isSelfEmployed(): bool { return $this->isSelfEmployed; } /** * Get isSelfEmployed * * @return boolean */ public function isSelfEmployed(): bool { return ($this->isSelfEmployed == 1 ? true: false); }
For DateTime parameters store the date as a string, and use the getters and setters to convert that string.
You may also need to create a DataTransformer if you are using the parameter in a form with the DateType form type.
Step 4: General Use
The bundle comes with an DoctrineEncryptListener. This listener catches the doctrine events onLoad, onFlush and postFlush.
The onLoad event listener will decrypt your entity parameter at loading. This means that your forms and form fields will already be decrypted.
The onFlush and postFlush event listeners will check if encryption is enabled, and encrypt the data before entry to the database.
So, in normal CRUD operation you do not need to do anything in the controller for encrypting or decrypting the data.
Step 5: Decrypt in services and controllers
You can of course inject the EncryptorInterface service any time into classes either by using autowiring or defining the injection in your service definitions.
<?php use PSolutions\EncryptBundle\Encryptors\EncryptorInterface; // Inject the Encryptor from the service container at class construction public function __construct(private readonly EncryptorInterface $encryptor) { } // Inject the Encryptor in controller actions. public function editAction(EncryptorInterface $encryptor) { ... // An example encrypted value, you would get this from your database query. $encryptedValue = "3DDOXwqZAEEDPJDK8/LI4wDsftqaNCN2kkyt8+QWr8E=<ENC>"; $decrypted = $encryptor->decrypt($encryptedValue); ... }
Or you can dispatch the EncryptEvent.
<?php ... use PSolutions\EncryptBundle\Event\EncryptEvent; use PSolutions\EncryptBundle\Event\EncryptEvents; use Symfony\Component\EventDispatcher\EventDispatcherInterface; ... public function indexAction(EventDispatcherInterface $dispatcher) { ... // An example encrypted value, you would get this from your database query. $event = new EncryptEvent("3DDOXwqZAEEDPJDK8/LI4wDsftqaNCN2kkyt8+QWr8E=<ENC>"); $dispatcher->dispatch(EncryptEvents::DECRYPT, $event); $decrypted = $event->getValue(); }
Step 5: Decrypt in templates
If you query a repository using a select with an array result then the doctrine onLoad event subscriber will not decrypt any encrypted values.
In this case, use the twig filter to decrypt your value when rendering.
{{ employee.bankAccountNumber | decrypt }}
Security Features
Authenticated Encryption (AES-256-GCM)
This bundle uses AES-256-GCM (Galois/Counter Mode), which provides:
- Confidentiality: Data is encrypted and cannot be read without the key
- Authenticity: Each ciphertext includes an authentication tag (16 bytes) that verifies it was created with the correct key
- Integrity: Any modification to the encrypted data (bit flips, insertions, deletions) will be detected during decryption and will cause an
EncryptException - Protection against bit-flipping attacks: Unlike CBC mode, GCM prevents attackers from modifying ciphertext in a controlled manner
- No padding oracle vulnerabilities: GCM is a stream cipher mode that doesn't use padding
Key Requirements
- The encryption key must be exactly 256 bits (32 bytes) after base64 decoding
- Keys are stored base64-encoded in your
.envor secrets vault - Generate a proper key with:
bin/console encrypt:genkey
Data Format
Encrypted values in the database have the following structure:
[base64( IV(12) + TAG(16) + ciphertext )] + '<ENC>'
- IV (Initialization Vector): 12 random bytes, unique for each encryption
- TAG (Authentication Tag): 16 bytes, computed from ciphertext + key
- ciphertext: The actual encrypted data
- '': Suffix marker to identify encrypted values
All components are necessary; removing or altering any part will cause decryption to fail.
Commands
You have already seen the command to generate a encryption key:
$ bin/console encrypt:genkey
You can decrypt/encrypt the entire database using the following
$ bin/console encrypt:database decrypt connection
The requried argument should be be decrypt or encrypt.
There is an option to define the database connection if you employ multiple connections in your application.
Migration from Previous Versions
From v2.0.x or earlier (CBC mode) to v2.1+ (GCM mode)
This is a mandatory migration if you have existing encrypted data.
The encryption format changed from CBC to GCM, making them incompatible. Follow these steps exactly:
Step 1: Decrypt all existing data
Using your current codebase (before updating the bundle), run:
php bin/console encrypt:database decrypt
This will convert all encrypted fields to plain text in the database.
⚠️ Verify that the data is now unencrypted (check your database directly).
Step 2: Update the bundle
composer update psolutions/encrypt-bundle
Or update your composer.json and run composer install.
Step 3: Re-encrypt with the new GCM method
With the updated bundle installed, run:
php bin/console encrypt:database encrypt
This will re-encrypt all plaintext fields using the new AES-256-GCM algorithm.
Step 4: Verify the migration
Check a few records in your database. Encrypted values should now:
- End with
<ENC>suffix - Be longer than before (due to IV + TAG added)
- Be base64-encoded strings
Also test your application workflows to ensure encryption/decryption works correctly.
Rollback Procedure
If something goes wrong after migration:
- Keep a backup of the database before starting migration
- If you need to roll back to the old version, you must first run
encrypt:database decryptwith the new version, then reinstall the old version and runencrypt:database encryptwith it
⚠️ Never skip the decryption step before updating, or you will permanently lose access to data encrypted with the old CBC format.
Custom Encryptors
You can create your own encryptor by implementing EncryptorInterface:
use PSolutions\EncryptBundle\Encryptors\EncryptorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class MyCustomEncryptor implements EncryptorInterface { private EventDispatcherInterface $dispatcher; private string $secretKey; public function __construct(EventDispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; } public function setSecretKey(string $key): void { $this->secretKey = $key; } public function encrypt(?string $data): ?string { // Your custom encryption logic // Must return null for null input // Must check for '<ENC>' suffix and return if present } public function decrypt(?string $data): ?string { // Your custom decryption logic // Must return null for null input // Must check for '<ENC>' suffix and return if not present } }
Then configure it:
psolutions_encrypt: encryptor_class: App\Encryptors\MyCustomEncryptor
⚠️ Security Warning: If you implement your own encryptor, you must ensure it provides proper authentication (like GCM's tag) to prevent tampering. Avoid CBC mode without HMAC.
Reference Implementation
See src/Encryptors/OpenSslEncryptor.php for the reference implementation using AES-256-GCM. It demonstrates:
- Proper IV generation (12 random bytes)
- Authentication tag handling (16 bytes)
- Base64 encoding of combined binary data
- Key validation (32 bytes after decoding)
- Error handling that doesn't leak information
For questions or issues, please open an issue on GitHub.
统计信息
- 总下载量: 20
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2024-02-06