A package for automatically encrypting and decrypting Eloquent attributes in Laravel 5.5+, based on configuration settings.
The purpose of this project is to create a set-it-and-forget-it package that can be
installed without much effort to encrypt and decrypt Eloquent model attributes stored
in your database tables, transparently. It is therefore highly opinionated but built
for configuration.
When enabled, it automagically begins encrypting
data as it is stored in the model attributes and decrypting data as it is recalled from
the model attributes.
All data that is encrypted is prefixed with a header so that encrypted data can be
easily identified, encryption keys rotated, and (optionally) versioning of the encrypted
data format itself.
This supports columns that store either encrypted or non-encrypted data to make migration
easier. Data can be read from columns correctly regardless of whether it is encrypted or
not but will be automatically encrypted when it is saved back into those columns. Standard
Laravel Eloquent features like attribute casting will continue to work as normal, even if
the underlying values stored in the database are encrypted by this package.
There is documentation for laravel-database-encryption
online,
the source of which is in the docs/
directory. The most logical place to start are the docs for the HasEncryptedAttributes
trait.
Framework | Version | Release | Status | PHP v7.1 | PHP v7.2 | PHP v7.3 |
---|---|---|---|---|---|---|
Laravel | v5.5 | v0.1.0 (Packagist) | Stable | |||
Laravel | v5.6 | v0.1.1 (Packagist) | Stable | |||
Laravel | v5.7 | v0.2.0 (Packagist) | Stable | |||
Laravel | v5.8 | v0.2.1 (Packagist) | Stable |
Encrypted values are usually longer than plain text values, sometimes much longer.
You may find that the column widths in your database tables need to be altered to
store the encrypted values generated by this package.
If you are encrypting long strings such as JSON blobs then the encrypted values may
be longer than a VARCHAR
field can support, and you will need to alter your column
types to TEXT
or LONGTEXT
.
The FAQ contains migration instructions if you are moving from elocryptfive.
Via Composer command line:
$ composer require austinheap/laravel-database-encryption
Or add the package to your composer.json
:
{
"require": {
"austinheap/laravel-database-encryption": "^0.2"
}
}
This package implements Laravel auto-discovery feature. After you install it the package
provider and facade are added automatically.
If you would like to declare the provider and/or alias explicitly, you may do so by first
adding the service provider to your config/app.php
file:
'providers' => [
//
AustinHeap\Database\Encryption\EncryptionServiceProvider::class,
];
And then add the alias to your config/app.php
file:
'aliases' => [
//
'DatabaseEncryption' => AustinHeap\Database\EncryptionFacade::class,
];
Publish the package config file:
$ php artisan vendor:publish --provider="AustinHeap\Database\Encryption\EncryptionServiceProvider"
You may now enable automagic encryption and decryption of Eloquent models by editing the
config/database-encryption.php
file:
return [
'enabled' => env('DB_ENCRYPTION_ENABLED', true),
];
Or simply setting the the DB_ENCRYPTION_ENABLED
environment variable to true, via
the Laravel .env
file or hosting environment.
DB_ENCRYPTION_ENABLED=true
Use the HasEncryptedAttributes
trait in any Eloquent model that you wish to apply encryption
to and define a protected $encrypted
array containing a list of the attributes to encrypt.
For example:
use AustinHeap\Database\Encryption\Traits\HasEncryptedAttributes;
class User extends Eloquent {
use HasEncryptedAttributes;
/**
* The attributes that should be encrypted on save.
*
* @var array
*/
protected $encrypted = [
'address_line_1', 'first_name', 'last_name', 'postcode'
];
}
You can combine $casts
and $encrypted
to store encrypted arrays. An array will first be
converted to JSON and then encrypted.
For example:
use AustinHeap\Database\Encryption\Traits\HasEncryptedAttributes;
class User extends Eloquent {
use HasEncryptedAttributes;
protected $casts = ['extended_data' => 'array'];
protected $encrypted = ['extended_data'];
}
By including the HasEncryptedAttributes
trait, the setAttribute()
and getAttributeFromArray()
methods provided by Eloquent are overridden to include an additional step. This additional step
simply checks whether the attribute being accessed via setter/getter is included in the $encrypted
array on the model, and then encrypts or decrypts it accordingly.
The key and encryption algorithm used is the default Laravel Encrypter
service, and configured in
your config/app.php
:
'key' => env('APP_KEY', 'SomeRandomString'),
'cipher' => 'AES-256-CBC',
If you’re using AES-256-CBC
as the cipher for encrypting data, use the built in command to generate
your application key if you haven’t already with php artisan key:generate
. If you are encrypting longer
data, you may want to consider the AES-256-CBC-HMAC-SHA1
cipher.
The IV for encryption is randomly generated and cannot be set.
This package has aggressive unit tests built with the wonderful orchestral/testbench
package which is built on top of PHPUnit. A MySQL server required for execution of unit tests.
There are code coverage reports for laravel-database-encryption
available online.
The following Laravel 5.5 methods from Eloquent are affected by this trait.
constructor()
– calls fill()
.fill()
– calls setAttribute()
which has been extended to encrypt the data.hydrate()
– TBD.create()
– calls constructor()
and hence fill()
.firstOrCreate()
– calls constructor()
.firstOrNew()
– calls constructor()
.updateOrCreate()
– calls fill()
.update()
– calls fill()
.toArray()
– calls attributesToArray()
.jsonSerialize()
– calls toArray()
.toJson()
– calls toArray()
.attributesToArray()
– calls getArrayableAttributes()
.getAttribute()
– calls getAttributeValue()
.getAttributeValue()
– calls getAttributeFromArray()
.getAttributeFromArray()
– calls getArrayableAttributes()
.getArrayableAttributes()
– extended to decrypt data.setAttribute()
– extended to encrypt data.getAttributes()
– extended to decrypt data.castAttribute()
– extended to cast encrypted data.isDirty()
– extended to recognize encrypted data.Yes! You can manually encrypt or decrypt data using the encryptedAttribute()
and decryptedAttribute()
functions. For example:
$user = new User();
$encryptedEmail = $user->encryptedAttribute(Input::get('email'));
No! You will not be able to search on attributes which are encrypted by this package because…it is encrypted.
Comparing encrypted values would require a fixed IV, which introduces security issues.
If you need to search on data then either:
SHA256
.You could store both a hashed and an encrypted value, using the hashed value for searching and retrieve
the encrypted value as needed.
User
model data?No! The same issue with searching also applies to authentication because authentication requires search.
elocryptfive
out-of-the-box?No! While it is a (more modern) replacement, it is not compatible directly out of the box. To migrate to this package from elocryptfive, you must:
composer.json
and run composer update
.A pull request for automated migrations is more than welcome but is currently out of the scope of this project’s goals.
insert-random-Eloquent-package-here
?Probably not! It’s not feasible to guarantee interoperability between random packages out there, especially packages that also heavily modify Eloquent’s default behavior.
Issues and pull requests regarding interoperability will not be accepted.
The following decently-trafficed sites use this package in production:
This is a fork of delatbabel/elocryptfive,
which was a fork of dtisgodsson/elocrypt,
which was based on earlier work.
Pull requests welcome! Please see
the contributing guide for more information.
The MIT License (MIT). Please see License File for more information.