Posted on under Laravel by Owen Conti.
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.
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.
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<?php2 3namespace Tests;4 5use OhSeeSoftware\LaravelAssertEncrypted\Traits\AssertEncrypted;6 7class SomeTest extends TestCase8{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 // Then12 $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.
Hopefully you found this article useful! If you did, share it on X!
Found an issue with the article? Submit your edits against the repository.