Search and Encrypt Data in Laravel with CipherSweet

Updated
featured-image.png

πŸ’‘ I included the link to the example repository at the conclusion of this tutorial.

Nowadays, ensuring the security and privacy of sensitive data is paramount. Data breaches can have severe consequences, including financial loss, reputational damage, and legal consequences. To protect against these threats, encryption has become an important aspect of modern web development.

CipherSweet is a backend library developed by Paragon Initiative Enterprises that provides a powerful framework for encrypting data in a way that balances security and performance.

Data Search on Encrypted Values

Traditional encryption methods transform plaintext data into ciphertext, which is not readable without the appropriate decryption key. While this ensures data confidentiality, it also complicates data search operations. When data is encrypted, standard search queries become ineffective because the encrypted data does not resemble the original plaintext.

To address this issue, CipherSweet provides a feature known as searchable encryption. This allows specific fields to be indexed in a way that enables efficient searching while maintaining encryption. The primary technique used for this purpose is the creation of blind indexes.

πŸ’‘ You may also want to check out Store Sensitive Data Safely With Encryption in Laravel to learn about basic encryption features in Laravel.

What is Blind Index

A blind index is a cryptographic construct that allows searches on encrypted data without revealing the plaintext. When you insert data into your database, a blind index is created by hashing and encrypting the plaintext value. This index is then stored alongside the encrypted data. During a search operation, the search term is processed in the same manner, and the resulting index is compared to the stored indexes.

πŸ’‘ Blind indexes usually support exact matching, not partial or fuzzy matching. This means a search for “John” will return no results for “Johnny”. Likewise with CipherSweet’s blind index.

Laravel CipherSweet

In this tutorial, we will learn how to perform basic encryption with CipherSweet using the Laravel CipherSweet package created by Spatie. Laravel CipherSweet is a wrapper for CipherSweet, so we can use it with Eloquent Laravel models easily.

By the end of this guide, you will have a basic understanding of how to protect your application data with the enhanced security provided by CipherSweet.

Implementation

Using Sodium PHP Extension

CipherSweet requires the Sodium extension, if you don’t have it, download it first. However, if you are using PHP version 7.2 and above, this extension should have been added by default. Once you have it, make sure to enable the extension in your php.ini by uncommenting it.

extension=sodium

Read also:

Database Setup

For example, let’s say we want to save a user’s ID Card Number and Driving License Number. So, here is the user migration:

// database\migrations\0001_01_01_000000_create_users_table.php

Schema::create('users', function (Blueprint $table) {
    // ...
    $table->string('id_card_number');
    $table->string('driving_license_number');
    // ...
});

πŸ’‘ Make sure that the columns intended for encryption are defined as string/text columns, because these columns will eventually store ciphertext instead of the actual values.

Additionally, in a real project, you should avoid storing values that aren’t used for numerical calculations in numeric columns.

Install the spatie/laravel-ciphersweet

Install the spatie/laravel-ciphersweet package via Composer:

composer require spatie/laravel-ciphersweet

After the that, you must publish and run the migrations with:

php artisan vendor:publish --tag="ciphersweet-migrations"
php artisan migrate

This will create a new table blind_indexes. This table will be used to store the hash index values ​​along with the morph column.

You can also publish the configuration file, but this is optional, just in case you want to update the base configuration:

php artisan vendor:publish --tag="ciphersweet-config"

Model Setup

Add the CipherSweetEncrypted interface and the UsesCipherSweet trait to the model to which you want to add the encrypted field.

You must also implement the configureCipherSweet method to configure CipherSweet.

// app\Models\User.php
// You can also add CipherSweet to other models

// ...
use ParagonIE\CipherSweet\BlindIndex;
use Spatie\LaravelCipherSweet\Contracts\CipherSweetEncrypted;
use ParagonIE\CipherSweet\EncryptedRow;
use Spatie\LaravelCipherSweet\Concerns\UsesCipherSweet;
// ...

class User extends Authenticatable implements CipherSweetEncrypted // implements this
{
    // use the trait
    use UsesCipherSweet;

    // ...
    // add this method
    public static function configureCipherSweet(EncryptedRow $encryptedRow): void
    {
        $encryptedRow
            // add the columns you want to encrypt the values ​​for
            ->addField('id_card_number')
            ->addField('driving_license_number')
            // add a blind index for each column you want to search
            ->addBlindIndex('id_card_number', new BlindIndex('id_card_number_index'))
            ->addBlindIndex('driving_license_number', new BlindIndex('driving_license_number_index'));
    }

    // ...
}

Generate Encryption Key

Next, create an encryption key that will be used to encrypt the value you want to encrypt. First, add this .env key to your .env file. The key value will be used by your application to read encrypted values.

CIPHERSWEET_KEY=

Next, run this command to generate the key:

php artisan ciphersweet:generate-key

Your CIPHERSWEET_KEY= should now have a very long random character value.

Encrypting Model Attributes

Now, you can start encrypting your model values:

php artisan ciphersweet:encrypt <your-model-class> <generated-key>

The <your-model-class> in this case is App\Models\User. The <generated-key> is the generated encryption key which is also the CIPHERSWEET_KEY.

You will notice that our columns, in this case id_card_number and driving_license_number are now encrypted, and the blind_indexes table is updated.

Read also:

Searching for Encrypted Columns

Now, even though the column is encrypted, we can still search for the value as long as we search for it in full text.

We can use the whereBlind and orWhereBlind scopes to search on blind indexes. This method accepts 3 arguments, the encrypted column, the index name you set up when calling addBlindIndex, and the raw value you want to search for. The package will search for the value using the blind index.

User::whereBlind('id_card_number', 'id_card_number_index', 'asdajsdjfhjfihwe')->first()

= App\Models\User {#5084
    id: 1002,
    name: "Mrs. Danika Zemlak",
    email: "oankunding@example.org",
    email_verified_at: "2024-07-12 00:17:29",
    #password: "$2y$12$ffDAarUyki6moaX89B4M8.Mh34HP75tus5Z9X2YGUaLNcDZdx7ojS",
    id_card_number: "asdajsdjfhjfihwe",
    driving_license_number: "sdjwhdw",
    #remember_token: "EQ0Onpn2Mw",
    created_at: "2024-07-12 00:17:29",
    updated_at: "2024-07-12 00:17:29",
}

Creating and Updating Records

Creating a new record works as usual, and the blind index will be updated automatically, allowing you to search values in the encrypted column. However, when updating an existing record, you cannot use (at least now) the standard $user->update($array) method. Instead, update it like this:

$user->id_card_number = 'loremipsum';
$user->save();

This approach ensures that the blind index is correctly updated along with the encrypted value.

Rotating keys

If an unauthorized person has access to your key or you simply want to rotate your key for security reasons, you can easily do so. First, create a new key and then run the ciphersweet:encrypt command again.

πŸ’‘ Before doing this, ensure that your old key still exists in the CIPHERSWEET_KEY entry in your .env file.

Run the following command to re-encrypt your data with the new key:

php artisan ciphersweet:encrypt "App\Models\User" <your-new-key>

After running this command, update the CIPHERSWEET_KEY in your .env file with the new key so that CipherSweet can decrypt your data again.

Conclusion

Nowadays, ensuring the security and privacy of sensitive data is critical. The Laravel CipherSweet package by Spatie provides a powerful and easy-to-use solution for encrypting data while maintaining search functionality. By leveraging advanced encryption techniques and blind index concepts, CipherSweet allows you to protect sensitive information without sacrificing the ability to perform efficient searches.

With this guide, you’ve learned how to install and configure the Laravel CipherSweet package, encrypt model attributes and create blind indexes, perform search queries on encrypted data, and rotate the encryption key to increase security.

By implementing these practices in your Laravel applications, you can significantly improve data security and ensure compliance with modern security standards. Secure your sensitive data with confidence and maintain the functionality your users expect.

πŸ’» The repository for this example can be found at fajarwz/blog-laravel-ciphersweet.

Fajarwz's photo Fajar Windhu Zulfikar

I'm a full-stack web developer who loves to share my software engineering journey and build software solutions to help businesses succeed.

Email me
Ads
  • Full-Stack Laravel: Forum Web App (Complete Guide 2024)
  • Flexible and powerful review system for Laravel, let any model review and be reviewed.

Share

Subscribe

Sign up for my email newsletter and never miss a beat in the world of web development. Stay up-to-date on the latest trends, techniques, and tools. Don't miss out on valuable insights. Subscribe now!

Comments

comments powered by Disqus