Storing and testing encrypted values in Laravel

under Laravel

When writing web applications, there are times where you'll need to store an encrypted value in the database. One example might be a secret token that you want extra security around in case your data is exposed. Furthermore, once you've stored the encrypted values, you'll probably want to test that they're actually stored encrypted instead of in plaintext.

Storing encrypted values

There's an open source package available written by Jeff Sagal, which will automatically encrypt values for specified database columns: Encryptable. Let's run through how you can use it.

Install the composer package:

  • composer require sagalbot/encryptable

Ensure you have an application key generated, skip this if you already have a key set in your .env file:

  • php artisan key:generate

Add the Encryptable trait to the model you want to encrypt a column on, and then add an $encryptable array to the model with the list of columns you want to store encrypted:

1<?php
2 
3...
4 
5use Sagalbot\Encryptable\Encryptable;
6 
7class User extends Authenticatable
8{
9 use Encryptable;
10 
11 protected $encryptable = ['secret_token'];
12 
13 ...
14}

Now whenever your model is saved with a secret_token value set, the secret_token value will be encrypted before being written to the database. When pulling the model from the database, the encrypted value will only be decrypted when either the property is accessed directly or through a toArray or toJson function.

1<?php
2 
3// Store value
4$user = User::create([
5 'secret_token' => 'abc123'
6]);
7 
8// Access directly
9$user->secret_token // outputs 'abc123'
10 
11// Access via toArray()/toJson()
12$user->toArray() // outputs [ ..., 'secret_token' => 'abc123' ]

Remember that if you dump the model without accessing the property, the value will be output as encrypted.

Testing encrypted values

Now that we have our value stored encrypted, we can write a test to confirm its stored encrypted and not in plaintext.

For this, we're going to use a package I wrote called Laravel Assert Encrypted. This package exposes a new assertion method for your tests to assert a database has an encrypted value in a specified column.

First, install the package:

  • composer require ohseesoftware/laravel-assert-encrypted

Add the AssertEncrypted trait from the package to your test class:

1<?php
2 
3namespace Tests;
4 
5use OhSeeSoftware\LaravelAssertEncrypted\Traits\AssertEncrypted;
6 
7class SomeTest extends TestCase
8{
9 use AssertEncrypted;

Use the new assertEncrypted method to test your encrypted value:

1<?php
2 
3/** @test */
4public function it_stores_users_secrets()
5{
6 // Given
7 $user = factory(User::class)->create([
8 'secret_token' => encrypt('api-key')
9 ]);
10 
11 // Then
12 $this->assertEncrypted('users', ['id' => $user->id], [
13 'secret_token' => 'api-key'
14 ]);
15}

The first argument is the table to query against, the second argument is the where data that should be used to find the row in the table, and the third argument is the encrypted data you're expecting.


Thanks for reading this article!

Hopefully you found this article useful! If you did, share it on Twitter!

Found an issue with the article? Submit your edits against the repository.