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?

Check out Laravel Security in Depth, where we dive into Laravel security concepts in depth each month, with weekly tips to fill in the gaps.

Leave a Reply

Your email address will not be published. Required fields are marked *