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:
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!
<?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:
<?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:
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
- Eloquent Mutator: Encrypted Casting (Laravel Documentation)
- Encryption Configuration (Laravel Documentation)
This entry is part 2 of 3 in the series Automatic Casting With Eloquent $casts
- How to Save JSON Data in Database in Laravel (With Example)
- How to Encrypt and Decrypt Model Data Using Casts in Laravel
- How to Search a JSON Column Using Laravel Eloquent
This was exactly what I needed! Thank you.
You’re welcome!
How to implemented search functionality for encrypted value? I already tried “where(‘user_name’, ‘like’, ‘%’ . $request->search . ‘%’)”
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.
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();
}
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.
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.