wazza / laravel-db-encryption
Production-ready Laravel package for secure, transparent encryption of sensitive model attributes with searchable hash indexes and key rotation support.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 2
Open Issues: 0
pkg:composer/wazza/laravel-db-encryption
Requires
- php: ^8.2 || ^8.3
- ext-json: *
- ext-openssl: *
- illuminate/console: ^12.0
- illuminate/database: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- fakerphp/faker: ^1.20.0
- laravel/legacy-factories: ~1
- mockery/mockery: ^1.2
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.8
- pestphp/pest-plugin-laravel: ^3.2
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-02-20 02:54:10 UTC
README
Laravel DB Encryption
A production-ready Laravel package for secure, transparent encryption of sensitive model attributes. Store encrypted data in a dedicated table while keeping your main tables clean and performant.
π Why This Package?
- Transparent Encryption: Automatic encryption/decryption via Eloquent trait
- Separate Storage: Encrypted data stored in dedicated
encrypted_attributestable - Searchable: Filter encrypted data using SHA-256 hash indexes
- Zero Configuration: Works out of the box with sensible defaults
- Production Ready: Comprehensive error handling, logging, and security features
- Performance: Batch operations and optimized queries
- Compliance Ready: Helps meet GDPR, HIPAA, PCI DSS requirements
π Requirements
- PHP: 8.2 or higher
- Laravel: 12.x
- OpenSSL: PHP OpenSSL extension
π Installation
1. Install via Composer
composer require wazza/laravel-db-encryption
2. Publish Configuration & Migrations
# Publish config file php artisan vendor:publish --provider="Wazza\DbEncrypt\Providers\DbEncryptServiceProvider" --tag="db-encrypt-config" # Publish migrations php artisan vendor:publish --provider="Wazza\DbEncrypt\Providers\DbEncryptServiceProvider" --tag="db-encrypt-migrations"
3. Generate Encryption Key
php artisan db-encrypt:generate-key
This adds DB_ENCRYPT_KEY to your .env file.
4. Run Migrations
php artisan migrate
π Usage
Basic Setup
Add the HasEncryptedAttributes trait to your model and define which attributes should be encrypted:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Wazza\DbEncrypt\Traits\HasEncryptedAttributes; class User extends Model { use HasEncryptedAttributes; protected $fillable = ['name', 'email', 'phone']; /** * Attributes that should be encrypted. * These MUST NOT exist as columns in your table! */ public array $encryptedProperties = [ 'social_security_number', 'credit_card_number', 'medical_record', ]; }
Working with Encrypted Attributes
// Create a user with encrypted attributes $user = User::create([ 'name' => 'John Doe', 'email' => 'john@example.com', 'social_security_number' => '123-45-6789', 'credit_card_number' => '4111-1111-1111-1111', ]); // Encrypted attributes work like normal properties echo $user->social_security_number; // '123-45-6789' (automatically decrypted) // Update encrypted attributes $user->social_security_number = '987-65-4321'; $user->save(); // Retrieve and use $user = User::find(1); echo $user->social_security_number; // Automatically decrypted!
Searching Encrypted Data
Use the whereEncrypted scope to search encrypted attributes:
// Find users by encrypted SSN $users = User::whereEncrypted('social_security_number', '123-45-6789')->get(); // Find specific user $user = User::whereEncrypted('credit_card_number', '4111-1111-1111-1111')->first(); // Combine with other queries $users = User::where('email', 'like', '%@example.com%') ->whereEncrypted('social_security_number', '123-45-6789') ->get();
Using the Facade
For direct encryption/decryption operations:
use Wazza\DbEncrypt\Facades\DbEncrypt; // Encrypt a value $encrypted = DbEncrypt::encrypt('sensitive data'); // Decrypt a value $decrypted = DbEncrypt::decrypt($encrypted); // Generate search hash $hash = DbEncrypt::hash('search value'); // Batch operations $encrypted = DbEncrypt::encryptBatch([ 'ssn' => '123-45-6789', 'cc' => '4111-1111-1111-1111', ]); // Verify without decrypting $isMatch = DbEncrypt::verify('original text', $encryptedValue);
π οΈ Advanced Features
Artisan Commands
Generate Encryption Key
# Generate and set new key in .env php artisan db-encrypt:generate-key # Just display the key without saving php artisan db-encrypt:generate-key --show
Re-encrypt Data (Key Rotation)
# Re-encrypt all encrypted attributes php artisan db-encrypt:re-encrypt # Dry run to see what would happen php artisan db-encrypt:re-encrypt --dry-run # Re-encrypt specific table only php artisan db-encrypt:re-encrypt --table=users # Process in smaller batches php artisan db-encrypt:re-encrypt --batch=50
Prune Orphaned Records
# Remove encrypted attributes for deleted models php artisan db-encrypt:prune # Dry run php artisan db-encrypt:prune --dry-run # Prune specific table php artisan db-encrypt:prune --table=users
Configuration
Edit config/db-encrypt.php:
return [ // Logging configuration 'logging' => [ 'level' => env('DB_ENCRYPT_LOG_LEVEL', 0), // 0=None, 1=High, 2=Mid, 3=Low 'indicator' => env('DB_ENCRYPT_LOG_INDICATOR', 'db-encrypt'), ], // Encryption key 'key' => env('DB_ENCRYPT_KEY'), // Database configuration 'db' => [ 'primary_key_format' => env('DB_ENCRYPT_DB_PRIMARY_KEY_FORMAT', 'int'), // 'int' or 'uuid' ], ];
Custom Exceptions
The package provides specific exceptions for better error handling:
use Wazza\DbEncrypt\Exceptions\EncryptionException; use Wazza\DbEncrypt\Exceptions\DecryptionException; use Wazza\DbEncrypt\Exceptions\InvalidAttributeException; use Wazza\DbEncrypt\Exceptions\ModelNotSetException; try { $encrypted = DbEncrypt::encrypt($value); } catch (EncryptionException $e) { // Handle encryption failure Log::error('Encryption failed: ' . $e->getMessage()); }
π Security Best Practices
1. Key Management
- Never commit encryption keys to version control
- Use different keys for each environment
- Rotate keys periodically using
db-encrypt:re-encrypt - Consider using a Key Management Service (KMS) in production
2. What to Encrypt
β Good candidates:
- Social Security Numbers
- Credit card numbers
- Passwords (though hashing is usually better)
- Medical records
- Personal identification numbers
- Private notes
β Bad candidates:
- Primary keys or foreign keys
- Data you need to sort by
- Data used in mathematical operations
- High-cardinality lookup values
3. Performance Considerations
- Encryption adds overheadβonly encrypt truly sensitive data
- Use indexes on your main tables for performance
- Consider caching decrypted data for read-heavy operations
- Use batch operations when encrypting/decrypting multiple values
4. Backup Strategy
- Always backup before key rotation
- Test key rotation in staging first
- Keep old keys until you verify re-encryption succeeded
See SECURITY.md for comprehensive security guidelines.
π§ͺ Testing
# Run all tests ./vendor/bin/pest # Run with coverage ./vendor/bin/pest --coverage # Run specific test file ./vendor/bin/pest tests/Unit/EncryptorTest.php
π Troubleshooting
"Encryption key is not set"
Solution: Run php artisan db-encrypt:generate-key or manually set DB_ENCRYPT_KEY in .env
"Cannot encrypt attribute 'name' because it exists as a column"
Solution: Encrypted properties must NOT exist as database columns. Remove the column or choose a different property name.
"Decryption failed"
Possible causes:
- Wrong encryption key
- Corrupted data
- Key was rotated but data wasn't re-encrypted
Solution:
- Verify
DB_ENCRYPT_KEYis correct - Check logs for detailed error messages
- Restore from backup if data is corrupted
Performance issues
Solutions:
- Ensure indexes are created on
encrypted_attributestable (done automatically) - Reduce logging level in production (
DB_ENCRYPT_LOG_LEVEL=0) - Use batch operations for multiple encryptions
- Consider caching frequently accessed encrypted data
Search not finding records
Check:
- Ensure you're using
whereEncrypted()scope - Verify the exact value (encryption is case-sensitive)
- Check that hash_index was generated correctly
π Monitoring
View Package Logs
# Real-time log monitoring tail -f storage/logs/laravel.log | grep db-encrypt # Search for errors grep "db-encrypt.*error" storage/logs/laravel.log
Check Encryption Status
use Wazza\DbEncrypt\Helper\Encryptor; // Get encryption info $info = Encryptor::getInfo(); /* [ 'method' => 'AES-256-CBC', 'hash_algorithm' => 'sha512', 'search_hash_algorithm' => 'sha256', 'iv_length' => 16, 'supported' => true, 'key_configured' => true, ] */ // Check if encryption is supported if (!Encryptor::isSupported()) { throw new Exception('OpenSSL not available'); }
π€ Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Write tests for your changes
- Ensure all tests pass (
./vendor/bin/pest) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
π License
This package is open-sourced software licensed under the MIT license.
π¨βπ» Author
Warren Coetzee
- Website: wazzac.dev
- Email: warren.coetzee@gmail.com
- GitHub: @wazzac
π Acknowledgments
- Built for the Laravel community
- Inspired by the need for simple, secure database encryption
- Thanks to all contributors and users
β Support
If this package helps you, consider buying me a coffee!
π Changelog
See CHANGELOG.md for what has changed recently.
π‘οΈ Security
Please review our security policy for reporting security vulnerabilities.
Made with β€οΈ for the Laravel community