Laravel Encrypting Model Data: You are doing it wrong!

TL;DR: Use bootable model traits to ensure you are encrypting/decrypting your data when saving/loading your data.

If an attacker gets access to your database, he can read or change important data such as a credit card number, or an account virtual balance. To mitigate damage in a disaster-scenario like that, you can encrypt your data.

Quick search on Google and I found this forum post with exactly what I need! Use a trait on your model and boom! Magic happens.

Image for post
Image for post
Code from Laracasts forum

Wrote my unit tests, and on first test run, blood and tears as most of my tests failed. Checking the error, Oh Sh*t… of course! I was encrypting an integer, and it became a loooong string after encryption. Ok, change the field types to text in the migration files and now it all works fine. All tests pass: I am creating users, checking the database… data is encrypted! Reading them back… nice, I have my precious integers once more. All good and marvelous with no more than 20 lines. Thanks Laravel!

A few days go by and I wrote a function to load all records in a certain model:

$data = Model::all()->toArray();

But the values for my encrypted fields were still… encrypted. WTF? It turns out that loading an Eloquent Model and converting the items into an array (or JSON) does not use the getAttribute($key) function from the model.

And this is where the example from Laracasts forum is flawed. You must use a Bootable Model Trait and listen for saving and retrieved events. Here is my Encrypted trait (picture for better readability, see the code block at the end of the article):

Image for post
Image for post
The CORRECT way for Encrypting Model Data

And as promised, the complete code for your copy & paste pleasure:

Trait:

<?phpnamespace App\Traits;use Illuminate\Support\Facades\Crypt;trait Encryptable{  public static function bootEncryptable()  {    static::retrieved(function ($model) {      foreach ($model->encryptable as $key) {        $value = $model->getAttribute($key);        $model[$key] = Crypt::decrypt($value);      }    });    static::saving(function ($model) {      foreach ($model->encryptable as $key) {        $value = $model->getAttribute($key);        $model[$key] = Crypt::encrypt($value);      }    });  }}

Usage:

class Account extends Model{  use Encryptable;
/**
* The attributes that should be encrypted on save. * * @var array */ protected $encryptable = [ 'balance', 'security_level' ];}

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store