Support the ongoing development of Laravel.io →

Encrypting Queued Jobs, Notifications, Mail, and Listeners in Laravel

6 May, 2024 14 min read

Photo by Shahadat Rahman on Unsplash

Introduction

There may be times when your Laravel application's queued jobs, notifications, mailables, or listeners contain sensitive information that you wouldn't want to have exposed. Typically, when storing sensitive information (such as passwords, API keys, etc.) in a database, you'd want to hash or encrypt the data.

Well, you can do the exact same thing with your queued jobs, notifications, mailables, and listeners!

In this article, we're going to quickly look at how to use the Illuminate\Contracts\Queue\ShouldBeEncrypted interface to encrypt your queued classes for improved security.

Encrypting Laravel Jobs

To understand why we'd want to encrypt your jobs, notifications, mailables, and listeners, let's take a look at a basic example.

We'll imagine our application requires a one-time password (OTP) when signing in. So we generate the OTP and send it to the user (whether this be via an email, SMS, etc.). We may have a job class that looks like so:

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendOneTimePassword implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct(public readonly string $oneTimePassword)
    {
        //
    }

    // ...
}

In the class above, we're not too bothered about how we're sending the OTP. We're more concerned about the argument that's been passed to the job. In this case, the one-time password.

To run the job, we'd do something like so (notice the 123456 that we're passing as the OTP):

\App\Jobs\SendOneTimePassword::dispatch('123456');

When we dispatch the job, the data is serialized and stored in the queue store (such as the database or Redis). It's held here until the queue worker pulls the job off the queue and processes it.

If you or anyone malicious were to access the queue store, they'd be able to see the following information (beautified for readability) about the pending job:

{
    "uuid": "3d05be68-8cd0-4c3a-8d05-71e86871713a",
    "displayName": "App\\Jobs\\SendOneTimePassword",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "App\\Jobs\\SendOneTimePassword",
        "command": "O:28:\"App\\Jobs\\SendOneTimePassword\":1:
                    {s:15:\"oneTimePassword\";s:6:\"123456\";}"
    }
}

Did you spot it?

The oneTimePassword property is visible in plain text in the data.command field! If someone were to access the queue store, they'd be able to see the OTP.

And this could be any other kind of sensitive information that you wouldn't want to be exposed, such as API keys, passwords, social security numbers, etc.

To prevent this, we can update our job class to implement the Illuminate\Contracts\Queue\ShouldBeEncrypted interface:

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendOneTimePassword implements ShouldQueue, ShouldBeEncrypted
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     */
    public function __construct(public readonly string $oneTimePassword)
    {
        //
    }

    // ...
}

As a result of using this interface, Laravel will now encrypt the data before storing it in the queue store, and then decrypt it when pulling the job off the queue to process it.

Let's take a look at what the job data might look like now after using the ShouldBeEncrypted interface:

{
    "uuid": "ea194df6-ebe1-42a6-9e46-b7ece3b61781",
    "displayName": "App\\Jobs\\SendOneTimePassword",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "App\\Jobs\\SendOneTimePassword",
        "command": "eyJpdiI6IjRpR1FuRG5NTWpMZ28vdytUaGE0Rmc9PSIsInZhbHVlIjoicERX
                    MnJieGVleHp3czhYb3drb1N3ZnpHdHd2MVB1OFYvK24xbmlEUHdMcHk4K2Rr
                    ekdQMk9uczVFWFBmVkYvc1NneXMwVWVndlBxQkZySThkaTV6bk5NaVAzWnpG
                    aTN2MTZkSEVOUElYQzFSMFMxSnl5NzBkL0V1R1JwUDRYVTUiLCJtYWMiOiI0
                    NTQ4MmU3NmFhMzg1MjZkODcxNDc5YzQyNzE5ZTFlOWQxNzE4OWNlNTE5ODRl
                    YmJkN2ZiMjljYzI5Nzc4ODIyIiwidGFnIjoiIn0="
    }
}

The oneTimePassword property is now encrypted in the data.command field! This means that even if someone were to access the queue store, they wouldn't be able to see the OTP.

Of course, this is a very basic example, but you can see how powerful this can be when dealing with sensitive information in your queued classes.

Encrypting Laravel Notifications

Not only can you use the Illuminate\Contracts\Queue\ShouldBeEncrypted interface with jobs, but you can also use it with queued notifications, mailables, and listeners.

This is great as it can add an extra layer of security to them too when they're stored in the queue store.

Let's take an example notification that could be used for sending a one-time password:

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SendOneTimePassword extends Notification implements ShouldQueue
{
    use Queueable;

    /**
     * Create a new notification instance.
     */
    public function __construct(public readonly string $oneTimePassword)
    {
        //
    }

    // ...
}

We can dispatch the notification like so (again, notice the 123456 that we're passing as the OTP):

use App\Notifications\SendOneTimePassword;

$user->notify(new SendOneTimePassword('123456'));

If we were to inspect the data stored in the queue store for the pending notification, we'd see the following:

{
    "uuid": "74e8c7d1-151d-4f05-aeae-b4dfcddba1a6",
    "displayName": "App\\Notifications\\SendOneTimePassword",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Notifications\\SendQueuedNotifications",
        "command": "O:48:\"Illuminate\\Notifications\\SendQueuedNotifications\":3:
                    {s:11:\"notifiables\";O:45:\"Illuminate\\Contracts\\Database
                    \\ModelIdentifier\":5:{s:5:\"class\";s:15:\"App\\Models\\User\"
                    ;s:2:\"id\";a:1:{i:0;i:1;}s:9:\"relations\";a:0:{}s:10:\"
                    connection\";s:5:\"mysql\";s:15:\"collectionClass\";N;}s:12:
                    \"notification\";O:37:\"App\\Notifications\\SendOneTimePassword
                    \":2:{s:15:\"oneTimePassword\";s:6:\"123456\";s:2:\"id\";s:36
                    :\"9494b2db-7362-4936-8e31-5179594a1c01\";}s:8:\"channels\";
                    a:1:{i:0;s:4:\"mail\";}}"
    }
}

Again, the oneTimePassword property is visible in plain text in the data.command field!

However, we can update our notification class to implement the Illuminate\Contracts\Queue\ShouldBeEncrypted interface:

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SendOneTimePassword extends Notification implements ShouldQueue, ShouldBeEncrypted
{
    // ...
}

This would now result in the pending notification's data being encrypted when stored in the queue store:

{
    "uuid": "6dc8206c-c06c-4f21-a495-659a61546864",
    "displayName": "App\\Notifications\\SendOneTimePassword",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Notifications\\SendQueuedNotifications",
        "command": "eyJpdiI6InV3V0V0dTM0eXRLa0xWdHpnajBqcEE9PSIsInZhbHVlIjoiTkd5M2
                    pVZUpSMFVOdlJEa0lDY2d4MzdLdE1URW5rUC9iZERoSGFkVEJ4M3ZMSERlcVhi
                    ZmViUEg4S1R6ZlZzQ2dIY0JQV2N0VXczWGs5dGE3Y3JUUkRwN2hVWkNTRlVtTz
                    FRQkVnMkVtM0VhWnBRd2U4d05mOS9EVzhuV29JK0pNYkx1cEtIb2hDbm5MbWdH
                    aVNrR3VPTjI3em5uMkFKbG00dVNRak5BRGFhRkZqeUtodDU0Q2tCaWNBamR2eU
                    1Ec2pDaTBoN0dSbEpRRnROczl5b09CVUlmT2laVDZVZTdxdU9PbGtwQlV1S0lz
                    RGRMdTlmVTRWRTZMNnRFbFczZ3NSb25TM0kyYmFyTTIyYmtwY3ZheTJibURhNX
                    dXQkV1ZjEwSStGRzFBN1gxQmRYZzRodXc3UEY4a05teCtPVU1sVG0yN3hOd3pB
                    TjdyOFluR0gxckdYeHdNZitYYmJMeUxlcm4yRUl5NVB2NHN4WmRrZzJhYVNiRX
                    cydzQ3WkM4YTFrZ1dIRnBqLzVndnJOc0U2R1JTUm0vdWVkVVBuUTcvUmcxOWVP
                    UER1SjN1VTR1cDQ1eDB1a0l0SWFTMHhQWCtEMkVjcDYzS1AvS042djlHTmo1eU
                    11NWdUcGl0dTlkSWVONVIwLzhOZ2M0SDdHQ1k2S0FJUWh4UTJzT3VRR2dESFpK
                    MERwZ1dMZUFrRy9aZU55TUdTN3I4SDJjcTQ1M0tjZS9CekZrb3p1OFFucU5YM0
                    pwTWN1Y1BjTWVCc3E0RDlaSlErSndNRnVZeFFtNExsbzFQSUR6Vmo0WGZSMXJN
                    TzNteXgvMEZnRGMxRU9CS1pBZUNEbnV1YVB1b0RZb25ENXQ3WjFuN3RZcHFLZ1
                    ByamxNa1dlTVV1bmZRWE5hcXd6UXpUZmlzOXc9IiwibWFjIjoiYzgyNjA5MGNk
                    NTA3ZmM2MGNiMGQ3MjFjYzQyNTk2MWI0OWZiMGVmMTUyODFjMjYwNDM1YzM1MW
                    MyZjY3YmY5YSIsInRhZyI6IiJ9"
    }
}

Encrypting Laravel Mailables

Similar to how we encrypted jobs and notifications, we can also encrypt mailables very easily.

Let's take this example mailable class that could be used for sending a one-time password:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class SendOneTimePassword extends Mailable implements ShouldQueue, ShouldBeEncrypted
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     */
    public function __construct(public readonly string $oneTimePassword)
    {
        //
    }

    // ...
}

We can send the mailable like so (again, notice the 123456 that we're passing as the OTP):

use App\Mail\SendOneTimePassword;
use Illuminate\Support\Facades\Mail;

Mail::send(new SendOneTimePassword('123456'));

If we were to inspect the data stored in the queue store for the pending mailable, we'd see the following:

{
    "uuid": "07d4671a-b350-4203-9152-d95d10398879",
    "displayName": "App\\Mail\\EncryptedMail",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Mail\\SendQueuedMailable",
        "command": "O:34:\"Illuminate\\Mail\\SendQueuedMailable\":15:{s:8
                    :\"mailable\";O:22:\"App\\Mail\\EncryptedMail\":2:{s:
                    15:\"oneTimePassword\";s:6:\"123456\";s:6:\"mailer\";
                    s:7:\"mailgun\";}s:5:\"tries\";N;s:7:\"timeout\";N;s:
                    13:\"maxExceptions\";N;s:17:\"shouldBeEncrypted\";b:0;
                    s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"
                    chainConnection\";N;s:10:\"chainQueue\";N;s:19:\"
                    chainCatchCallbacks\";N;s:5:\"delay\";N;s:11:\"afterCommit\"
                    ;N;s:10:\"middleware\";a:0:{}s:7:\"chained\";a:0:{}s:3
                    :\"job\";N;}"
    }
}

Just like with the jobs and notifications, the oneTimePassword property is visible in plain text in the data.command field!

But we can update the mailable class to implement the Illuminate\Contracts\Queue\ShouldBeEncrypted interface:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class SendOneTimePassword extends Mailable implements ShouldQueue, ShouldBeEncrypted
{
    // ...
}

This would now result in the pending mailable's data being encrypted when stored in the queue store:

{
    "uuid": "e1296554-44b3-4bbb-b5ff-daa6f1660861",
    "displayName": "App\\Mail\\EncryptedMail",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Mail\\SendQueuedMailable",
        "command": "eyJpdiI6Im1PQUZUWlFmMllYUnNLZmJ2cUxFTlE9PSIsInZhbHVlIjoiUG1SSkJ
                    kY1NpdmpsNTVhc0U3dHYzcXFabFUxdkw5blZQSlU4OGw5eHNCNy9DUjlhUFEzZl
                    V3UGFTRVpiNitSaGZFUElZN0xITEcwYnQzMFZtSGk0bGFVdW5GcWNBVlh2K0Vja
                    FA1dUFmcmVFa1RqNEhDSmthSVJ0c3RweUhlUEJRQTNzUWpmZHhtTVZxVGZSZER3
                    d1VvZURETFpEb2lPL1VENUhTTjU4eEY2Mk4vemhRL29RTVE4TGVQNklOazAwY1J
                    wOFRjMTUydVZZZmd4aWZCY2xQUUNvMmw4bXhhczgvYThCV1dnaENZaURFTWw1OG
                    kybEg2cFVYaDZSS3ZRMERKMzJZZDB4M1c0ZXN0bThJNG1oNlNrdVpGeUZmN2crM
                    Wl6c09oVkNIN2Y2QVN4dkNXM0hzMGh0N3pDakxpZWpmbjJ2VldHdVdPWWtKcnph
                    WGRPUW9POTdQN0syVXNWa0ZUa000SmdrelVFQ1hrUkNrYUV3SXhkcGJGamMwZEV
                    XSjRpOWJhZ25FL0NUZ0lpeERWcG9YWjNkT3ozeEJCVG5LTmVoNzB0bkVPeUs0dm
                    JxQ2VCRzhsVk42TGF1Y2djYWsxc0ZtQjdPbWtVanF3c3p4cjY5b3d6VTFZRkVPV
                    mI1SDlJbGRKeHN6Sy9BOTF1K0UrZnJvNjNja0JXME55ZlBNY1FCWkJ6U3BJbnVE
                    YkJzZFdVNFE1aHdCUDNBTzdva3RBdFkzeDA1WExGTlRacys1V01pNTNzOVU4U2R
                    ZYUduRHBVSE5lQ3FnSVBQZXVBb2VLSzlKVzVhSkJLaWZKS3gzZm5KUWR3UXRLND
                    0iLCJtYWMiOiI1NjEyZjljMmZjNjlmMWI3NmIzYjQxM2MwMmQyNWM0YmIxODg4Z
                    jNlNGM5ODAyOTk4YmRiYzYzOGZjNjZhYjU0IiwidGFnIjoiIn0="
    }
}

Encrypting Laravel Listeners

Finally, we can also encrypt listeners using the Illuminate\Contracts\Queue\ShouldBeEncrypted interface.

Let's imagine we have this event that accepts a one-time password in the constructor:

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MyAwesomeEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     */
    public function __construct(public readonly string $oneTimePassword)
    {
        //
    }

    // ...
}

We'll then have a queued listener that listens for the event:

namespace App\Listeners;

use App\Events\MyAwesomeEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class QueuedListener implements ShouldQueue
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(MyAwesomeEvent $event): void
    {
        //
    }
}

It's worth noticing that in this case, it's not actually the listener that directly contains the sensitive data. It's the event that contains the sensitive data.

We can dispatch the event like so (once again, notice the 123456 that we're passing as the OTP):

use App\Events\MyAwesomeEvent;
use Illuminate\Support\Facades\Event;

Event::dispatch(new MyAwesomeEvent('123456'));

Sure enough, if we were to inspect the data stored in the queue store for the pending listener, we'd see the following:

{
    "uuid": "e395f426-1ae5-463b-bf27-6340528dc845",
    "displayName": "App\\Listeners\\QueuedListener",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Events\\CallQueuedListener",
        "command": "O:36:\"Illuminate\\Events\\CallQueuedListener\":20:{s:5:\"
                    class\";s:28:\"App\\Listeners\\QueuedListener\";s:6:\"
                    method\";s:6:\"handle\";s:4:\"data\";a:1:{i:0;O:25:\"App\\
                    Events\\MyAwesomeEvent\":1:{s:15:\"oneTimePassword\";s:6:\"
                    123456\";}}s:5:\"tries\";N;s:13:\"maxExceptions\";N;s:7:\"
                    backoff\";N;s:10:\"retryUntil\";N;s:7:\"timeout\";N;s:13:\"
                    failOnTimeout\";b:0;s:17:\"shouldBeEncrypted\";b:0;s:3:\"
                    job\";N;s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"
                    chainConnection\";N;s:10:\"chainQueue\";N;s:19:\"
                    chainCatchCallbacks\";N;s:5:\"delay\";N;s:11:\"afterCommit
                    \";N;s:10:\"middleware\";a:0:{}s:7:\"chained\";a:0:{}}"
    }
}

However, we can update the QueuedListener class to implement the Illuminate\Contracts\Queue\ShouldBeEncrypted interface:

namespace App\Listeners;

use App\Events\MyAwesomeEvent;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class QueuedListener implements ShouldQueue, ShouldBeEncrypted
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(MyAwesomeEvent $event): void
    {
        //
    }
}

As a result, the pending listener's data will now be encrypted when stored in the queue store:

{
    "uuid": "f6e2d019-95b0-4385-9c8d-9004d998388c",
    "displayName": "App\\Listeners\\QueuedListener",
    "job": "Illuminate\\Queue\\CallQueuedHandler@call",
    "maxTries": null,
    "maxExceptions": null,
    "failOnTimeout": false,
    "backoff": null,
    "timeout": null,
    "retryUntil": null,
    "data": {
        "commandName": "Illuminate\\Events\\CallQueuedListener",
        "command": "eyJpdiI6InJobm5sSTFxdnpueUR3T2k3eHF1NWc9PSIsInZhbHVlIjoiWi9aMGJ
                    ub0orNmtZbmVtWEVUT1l6T09XU2NvdUxxb3pSZW5PNFlibnFSTWNteHJtU255bN
                    STBXamlFVVl3UzgrRDBxSkw5UzdHbHF2QmdWenJBL3FPZitaQ3FMRURlcnBVbFB
                    U4em1tS0NmNWMreUM2R0hTMEwxN0Z2WFEwVnkzQmZjR0NjZ1pOSGt6TzlsRTUOH
                    MGRIZlFud0h4RzVFSmliUlBnamhrOCtMUWpFRW5JSlVvdTNnUE1OWGRleGhMENq
                    M4Uk0zdjE3SU8xbVRERFBGOE1meUlrSHZramhYSWZXNkdEanNBVFlDcVUy3NkeE
                    RGZCcVlDRlFSM09WNlNSdjRZSTJTUHZWdFhUU2thTXpYQUtZa2dPSlQ2SZrS21W
                    phZEo1S0h2QmFYRlVhR0wzVldEcmxJYzBqbG5HZFdRc25zU2xmNnNNVSIOHIrQ2
                    UXFJcDZFZ29LSTcxQThsV0l2S0l3S1RqcGtvRTVtWnZ4VUdwcTNvNjgQUpubjRr
                    lmZ3dCbGdjNkpzQTIyZ3A5ZnJmY2tZdU1JU3hQaUVISlM4aXJnNk1xC9HZk1QRE
                    Vk1VWGRvL0R0ek1RcURyc1hKZjY2UFZjVExkVm1NSHpSVnFNSGRLchOR1Jyc2Uz
                    RzSURGL09sUnZ1ekNMYnQ5b2dUL0dMMGdJU0JkN2MraFhPTlJrR2yMEMrRndOVW
                    RTFKZ0RPTFBidWJ6SlhJMnpheVQyanNvOUFEWnlhWk1QN1g1NHheEVhQmd1amVG
                    h4SjhhT0FrWnRDNXZIT1dncUdLY1hNMGRydWNyTi9KZjhtdVZEEtxN1BMd2hRMz
                    dGFvcE5MUElybngzOGtvZzBqSzZNSncrWnNBbDA0U0h1QkMvdBoUm93Wk9VT0Y3
                    FZM2Eya0xzaWxTK0hmSlJxa0JQa3RxeTAva2UxSWhYM2FyMmqZDJRNUhCbFFNVG
                    d2xMYXJoSiswU0FNcmVQRy9IIiwibWFjIjoiMTg4NjdhZjUY2MxMGIwZGRmNTgy
                    lkOTAxNDZjN2EwNWI1MGZjMjIyMmE5ZTNmYzg3NDdlNTg0TVlY2I0MyIsInRhZy
                    IiJ9"
    }
}

Should I Encrypt My Laravel Jobs?

Encrypting your queued classes can be a great way to add an extra layer of security to your applications. It can help protect sensitive information from being exposed if someone were to access the queue store.

And the best part is that it's super easy to do (as we've seen above)! All you need to do is implement the Illuminate\Contracts\Queue\ShouldBeEncrypted interface in your queued classes.

However, it's worth noting that encrypting and decrypting data will add some overhead to your application. Now, I don't actually have any benchmarks to say how big or small this overhead is. It could potentially be so small that it's negligible. On the other hand, it could be something that has a noticeable impact on your application's performance, especially for larger applications.

If I manage to get any benchmarks, I'll be sure to update this article with the results.

But at a bare minimum, I'd recommend encrypting any queued classes that contain sensitive information. This could be things like passwords, API keys, social security numbers, etc.

If you do some experimenting and find that the overhead isn't too much of a performance hit, then you might want to consider encrypting all your queued classes just for consistency. But again, this is something you'd need to test and see how it affects your application. So it's not a one-size-fits-all answer.

Further Reading

If you're interested in finding out about other ways to protect sensitive information that's being passed around your application, you might also want to check out my "Redacting Sensitive Parameters in PHP" article.

It covers how to use PHP's built-in #[\SensitiveParameter] to redact sensitive information from logs and stack traces.

Conclusion

Hopefully, this article has given a quick insight into how you can use the Illuminate\Contracts\Queue\ShouldBeEncrypted interface to encrypt your queued Laravel jobs, notifications, mailables, and listeners for improved security.

If you enjoyed reading this post, I'd love to hear about it. Likewise, if you have any feedback to improve the future ones, I'd also love to hear that too.

You might also be interested in checking out my 220+ page ebook "Battle Ready Laravel" which covers similar topics in more depth.

Or, you might want to check out my other 440+ ebook "Consuming APIs in Laravel" which teaches you how to use Laravel to consume APIs from other services.

If you're interested in getting updated each time I publish a new post, feel free to sign up for my newsletter.

Keep on building awesome stuff! 🚀

Last updated 3 weeks ago.

driesvints liked this article

1
Like this article? Let the author know and give them a clap!
ash-jc-allen (Ash Allen) I'm a freelance Laravel web developer from Preston, UK. I maintain the Ash Allen Design blog and get to work on loads of cool and exciting projects 🚀

Other articles you might like

November 18th 2024

Laravel Custom Query Builders Over Scopes

Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...

Read article
November 19th 2024

Access Laravel before and after running Pest tests

How to access the Laravel ecosystem by simulating the beforeAll and afterAll methods in a Pest test....

Read article
November 11th 2024

🍣 Sushi — Your Eloquent model driver for other data sources

In Laravel projects, we usually store data in databases, create tables, and run migrations. But not...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.