Store Sensitive Data Safely With Encryption in Laravel

Updated
featured-image.png

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

Encryption is the process of converting data into a format that is unreadable to unauthorized users. This ensures the confidentiality and security of the data. Encrypted data can only be decrypted and read by someone who has the proper decryption key. Data encryption protects sensitive information such as user passwords, personal data, and financial information.

Laravel provides built-in support for encryption to help developers easily secure their applications. Laravel’s encryption services are based on the OpenSSL library and offer a straightforward way to encrypt and decrypt data.

Notes Regarding Encryption in Laravel

There are several notes that we should pay attention to regarding data encryption in Laravel:

  1. Make sure the column you want to encrypt is a string/text column, even though all the data values ​​you want to encrypt are numbers.
  2. All encrypted values are encrypted using OpenSSL and the AES-256-CBC cipher and signed with a Message Authentication Code (MAC).
  3. The encryption algorithm is based on your application’s APP_KEY value in your .env file. You must keep your APP_KEY secure. If someone gets access to it, they can decrypt all your encrypted data.
  4. If you lose your APP_KEY, you cannot decrypt your data. This is because this key is used for encryption and decryption.
  5. Your APP_KEY is regenerated every time you run php artisan key:generate. So don’t run key:generate unless you want your keys changed (like key rotation).
  6. If you change your application’s encryption key, all authenticated user sessions will be logged out of your application. This is because every cookie, including session cookies, is encrypted by Laravel.
  7. If you need to rotate your application’s encryption key, you need to manually re-encrypt the encrypted attributes using the new key. But if you don’t want to re-encrypt the data yet, you can include the previous encryption key in your app’s APP_PREVIOUS_KEYS environment variable. When you set this environment variable, Laravel will always use the “current” encryption key when encrypting values. However, when decrypting a value, Laravel will try the current key first, and if decryption fails using the current key, Laravel will try all previous keys until one of the keys is able to decrypt the value. The APP_PREVIOUS_KEYS may contain a comma-separated list of all your previous encryption keys:
APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="
  1. Since the value is encrypted, we will not be able to query or search for it in the database using its raw value, unless, we decrypt all the values ​​in the database one by one and check if any of the results match what we are looking for.

Read also:

How to Encrypt and Decrypt Data in Laravel

We can utilize encryptString method provided by the Crypt (Illuminate\Support\Facades\Crypt) facade or using the encrypt helper to encrypt a string.

use Illuminate\Support\Facades\Crypt;

Crypt::encryptString($request->token),

// or using the global helper
encrypt($value)

To decrypt the encrypted value, we can use the decryptString method provided by the same facade or using the decrypt helper.

Crypt::decryptString($encryptedValue);

// or using the global helper
decrypt($encryptedValue)

If the value cannot be decrypted, such as when the value is not an encrypted value, DecryptException (Illuminate\Contracts\Encryption\DecryptException) will be thrown.

use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;

try {
    $decrypted = Crypt::decryptString($encryptedValue);
} catch (DecryptException $e) {
    // ...
}

Encrypted Casting

In our model, we can add the column we want to encrypt in the $casts property so that its value can be automatically encrypted when we store the value and also automatically decrypted when we retrieve it. No need to manually encryptString and decryptString the value.

// put it in your model, like User model, or UserDetail model, etc.

protected function casts(): array
{
    // for example, I have these columns on my users table:
    // $table->string('id_card_number');
    // $table->string('driving_license_number');

    return [
        'id_card_number' => 'encrypted',
        'driving_license_number' => 'encrypted',
    ];
}

Search The Encrypted Data Using Its Raw Values

Using this implementation, you can’t. But as I said, as a workaround, you can decrypt all the values ​​in the table one by one and check if they match the value you are looking for.

User::all()->first(fn ($user) => $user->id_card_number === '8274819283712381')

// or using chunk

$user = null;
User::chunk(500, function ($users) use (&$user) {
    $user = $users->first(fn ($user) => $user->id_card_number === '8274819283712381');
    if ($user) return true;
});
$user;

= App\Models\User {#5081
    id: 1002,
    name: "Shanel Hintz",
    email: "fredrick17@example.com",
    email_verified_at: "2024-07-10 20:28:01",
    #password: "$2y$12$T.hODmB8zvpIrKWpcLQOWO3kVX4Nc.afPlFow3B3f/QEZyPipJGqK",
    id_card_number: "eyJpdiI6IkI4aVR0MVIwNXMvQkhSR1BZWUpWZFE9PSIsInZhbHVlIjoid0Eva0hlVjVyaExwcnZhYTNMVFZIQT09IiwibWFjIjoiMTNkOTI0ODkxNGFhNzJhMmQ5ZjU2ODY0ZGZmYTMyNGQyYmEyOWI2Y2Y0YTRmMGFjMWNhYmRiZTU5ZjlhYjhlZSIsInRhZyI6IiJ9",
    driving_license_number: "eyJpdiI6IlNFTjZDcDI4QlhWQVlEbUpYdXFmSEE9PSIsInZhbHVlIjoiNDR3Mk9QanNrUEdWNXp4a210RlNpZz09IiwibWFjIjoiYTU0NWM3MTNjOWEwZDJiYzAxNTgzNjM4ZTk3NjRkYTZjMWE1ZWNkNjRkOGMzMWY5OWQ5Yjg0MDFlNzVlZDg3MCIsInRhZyI6IiJ9",
    #remember_token: "QRevtXPcuC",
    created_at: "2024-07-10 20:28:03",
    updated_at: "2024-07-10 20:28:03",
  }

Read also:

In the next article, I’ll introduce a more effective approach for encrypting data while enabling seamless search functionality.

πŸ’‘ Check out the article Search and Encrypt Data in Laravel with CipherSweet to learn how to encrypt and search data securely in Laravel.

Conclusion

In this article, we’ve explored the basics of encryption in Laravel, highlighting the importance of data encryption and how Laravel’s built-in support simplifies this process. We’ve seen how to configure and use Laravel’s encryption functions, including encrypting and decrypting data using the Crypt facade and leveraging encrypted transmission in the model for automatic encryption and decryption.

Additionally, we cover key considerations for encryption in Laravel, such as the importance of keeping your APP_KEY secure, the implications of changing your encryption key, and the challenges associated with querying encrypted data.

By following these practices, you can improve the security of your Laravel applications and ensure that sensitive information remains protected.

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

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