Categories
Development Security

Using a Custom Key for Encrypted Cast Model Attributes in Laravel

This is the first security tip from my Laravel Security in Depth newsletter, sent out on Monday to all subscribers. Please sign up if you’d like these tips delivered weekly.

Laravel allows you to cast model attributes as encrypted strings, when stored in the database. This gives you added security for any values that are sensitive (such as Personally identifiable information, PII), including complex structures such as arrayscollections, and even objects.

class User extends Model 
{
    protected $casts = ['secrets' => 'encrypted:array']; 
}

The downside is, Laravel uses the default application encryption key to encrypt the value. This opens up the potential risk of a hacker being able to encrypt a specially crafted payload and then retrieve the encrypted value somehow (as I did in my Laracon talk to gain a root shell).

The solution is quite easy, we can use the Model::encryptUsing() method, which was added to Laravel by Illia Sakovich. It allows us to define a custom encrypter for use by the Model when encrypting data, which lets us use a different encryption key.

First, generate a new encryption key and add it into .env:

php artisan key:generate --show

Next, load that into your application config – something like database.encryption_key makes sense, given it’s for the database model encryption.

Finally, update your AppServiceProvider to load the new encryption key into the model:

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Encryption\Encrypter;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $key = $this->databaseEncryptionKey();
        $cipher = config('app.cipher');
        Model::encryptUsing(new Encrypter($key, $cipher));
    }

    protected function databaseEncryptionKey(): ?string
    {
        $key = config('database.encryption_key');
        return base64_decode(Str::after($key, 'base64:'));
    }
}

You can find all the code in this Gist: https://gist.github.com/valorin/ce58cf55dedaf759b3aa7fcfb2fcf613.

That should do it – but don’t change your key after data is stored in the database, or you’ll lose access to the original data with the old key!

Your encrypted casts should now use a custom key, keeping your application key safe.

Interested in learning more?

If you want to take a deeper dive into security, become a Laravel Security in Depth subscriber to receive monthly In Depth emails about Laravel Security concepts, and access our intentionally vulnerable demo site, plus weekly security tips to help you write secure code.

In the past we have covered Using Placeholders Safely, Escaping Output Safely and SQL Injection, as well as how Encryption works in Laravel, plus you can access all past emails, so you can learn about the topics that interest you today.

Leave a Reply

Your email address will not be published.