π‘ 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.