How to Encrypt and Decrypt Model Data Using Casts in Laravel

Using the Eloquent ‘encrypted’ cast type, you can instruct Laravel to encrypt specific attributes before storing them in the database. Later, when accessed through Eloquent, the data is automatically decrypted for your application to use.

Encrypting fields in a database enhances security by scrambling sensitive data. This measure shields information like emails, addresses, and phone numbers, preventing unauthorized access and maintaining confidentiality even if data is exposed.

In this guide you’ll learn to use Eloquent’s built-in ‘encrypted’ cast to encrypt sensitive data within an ‘Employee’ model to ensure personal data is stored securely.

Important note: Encryption and decryption in Laravel are tied to the APP_KEY found in the .env file. This key is generated during the installation and should remain unchanged. Avoid running ‘php artisan key:generate‘ on your production server. Generating a new APP_KEY will render any encrypted data irretrievable.

While keeping that in mind. Let’s get started and apply encryption!

Step 1: Create a Laravel Project

Begin by creating a new Laravel project if you haven’t done so already. Open your terminal and run:

composer create-project laravel/laravel model-encrypt
cd model-encrypt

Step 2: Add Database Credentials to the .env file

Open the .env file in your project and add the database credentials you wish to use:

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your-db
DB_USERNAME=your-db-user
DB_PASSWORD=your-db-password

Step 3: Create a Model and Migration

Begin by generating an Employee model and its corresponding migration using Artisan commands:

php artisan make:model Employee -m

Step 4: Add Migration Code

Open the generated migration file and add the code below to define the table and its columns, including those that we will apply encryption to later on.

As stated in the documentation on Eloquent encrypted casting all columns that will be encrypted need to be of type ‘text’ or larger, so make sure you use the correct type in your migration!

database/migrations/2023_12_10_172859_create_employees_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('employees', function (Blueprint $table) {
            $table->id();
            $table->string('name'); // The 'name' column which we won't encrypt
            $table->text('email'); // The 'email' column, which we will encrypt
            $table->text('phone'); // The 'phone' column, which we will encrypt
            $table->text('address'); // The 'address' column, which we will encrypt
            // Other columns...
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('employees');
    }
};

Step 5: Run the Migration

Run the migration to create the ‘employees’ table:

php artisan migrate

Step 6: Add encrypted casts to Model

Open the Employee model and add the code below to specify the attributes to be encrypted using the $casts property. We’ll also define a fillable array to make sure fields will support mass assignment to ease creation of models with data:

app/Models/Employee.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Employee extends Model
{
    protected $casts = [
        'email' => 'encrypted',
        'phone' => 'encrypted',
        'address' => 'encrypted',
        // Other sensitive attributes...
    ];

    protected $fillable = [
        'name',
        'email',
        'phone',
        'address',
    ];

    // Other model configurations...
}

Step 7: Saving and Retrieving Encrypted Data

Once configured, saving and retrieving data from the encrypted attributes remains unchanged. Eloquent will automatically handle the encryption and decryption processes.

To test this out I like to use Laravel tinker. To follow along open Tinker by running:

php artisan tinker

Then paste the following code:

use App\Models\Employee;

$employee = Employee::create([
    'name' => 'Paul Atreides',
    'email' => 'paul@arrakis.com', // Data is encrypted before storing
    'phone' => '123-456-7890', // Encrypted before storing
    'address' => 'The Keep 12', // Encrypted before storing
]);

echo $employee->email; // Automatically decrypted
echo $employee->phone; // Automatically decrypted
echo $employee->address; // Automatically decrypted

The output shows the Laravel Eloquent was able to decrypt the contents properly:

> echo $employee->email;
paul@arrakis.com
> echo $employee->phone;
123-456-7890
> echo $employee->address;
The Keep 12

If we view the contents in our database we can verify that the sensitive data in email, phone and address is in fact encrypted:

Screenshot of HeidiSQL Showing Encrypted Data in employees Table
Screenshot of HeidiSQL Showing Encrypted Data in employees Table

Conclusion

By using Laravel Eloquent’s built-in cast “encrypted” we can easily add a layer of security that applies encryption to sensitive data.

In our example we learned how to encrypt sensitive data of employee’s like email, address and phone and demonstrated how the application can still use them.

Now you can apply this technique to your own applications and ensure the privacy of your users is up to todays standards. Happy coding!

References

This entry is part 2 of 3 in the series Automatic Casting With Eloquent $casts

  1. How to Save JSON Data in Database in Laravel (With Example)
  2. How to Encrypt and Decrypt Model Data Using Casts in Laravel
  3. How to Search a JSON Column Using Laravel Eloquent

Johan van den Broek

Johan is the creator of laracoding.com. As a child, he began tinkering with various programming languages, many of which have been long forgotten today. Currently, he works exclusively with PHP and Laravel, and his passion for programming remains to this day.

7 thoughts on “How to Encrypt and Decrypt Model Data Using Casts in Laravel

  1. How to implemented search functionality for encrypted value? I already tried “where(‘user_name’, ‘like’, ‘%’ . $request->search . ‘%’)”

    1. Unfortunately it is not possible to search a value in an encrypted column using a where condition that is executed in an SQL query. You can loop and check for matches in your PHP code, but it will be at a considerable performance hit. Overall it is best not to crypt columns at all when you need to search or order them.

      1. It is possible, after spending few time I got the solution by using the below conditions:

        /* Assign the customer records in an array. The “encrypted” logic that when we fetch the
        records it decrypts so I assign it in an array. */
        $search_customer = [];
        $all_customer = Customer::all();
        if (count($all_customer)) {
        foreach ($all_customer as $key => $cust) {
        $search_customer[$key] = array(
        ‘id’ => $cust->id,
        ‘user_name’ => $cust->user_name,
        );
        }
        }

        // Implement it in search logic

        if ($request->search != null) {
        $search = $request->search;
        // create an empty array where we assign the ID of the matching record
        $matching_ids = [];
        if (count($search_customer)) {
        foreach ($search_customer as $item) {
        if (stripos($item[‘user_name’], $search) !== false) {
        $matching_ids[] = $item[‘id’];
        }
        }
        }
        if (count($matching_ids))
        $members = Customer::whereIn(‘id’, $matching_ids)->get();
        }

        1. Thanks for contributing this code snippet. This may be useful for some of the readers here 🙂

          However, be aware this is not executed in an SQL Query. The code fetches all records using the Customer Eloquent Model and loops results in PHP and compares each record one by one with the decrypted user_name value. In case you have many records it probably consumes quite a bit of memory and won’t be fast.

  2. Yes, you are correct it takes some time if heavy data is available so for that, we need to think of other functionalities but till it works.

Leave a Reply

Your email address will not be published. Required fields are marked *

Recent Posts