tillythecoder.com
tillythecoder.com
Encrypting Values in Laravel 12 — A Complete, Modern Guide
Tilly The Coder

Encrypting Values in Laravel 12 — A Complete, Modern Guide

Tilly The Coder · ·
4 mins
This is the estimated time it takes to read the article.
Laravel

Data security isn’t a bolt‑on feature; it’s a first‑class requirement. Laravel 12 ships with a polished OpenSSL‑powered encrypter (AES‑256‑CBC by default) and several developer‑friendly APIs that make it almost effortless to protect sensitive information. All encrypted payloads are signed with a message‑authentication code (MAC), so any tampering is detected during decryption.

1 · Initial Setup

1.1 Generate—and protect—your APP_KEY

Laravel will refuse to encrypt or decrypt anything until a valid 32‑byte base‑64 key is present in .env:

php artisan key:generate       # populates APP_KEY

Commit only .env.example; keep real keys in a secret manager. If the key leaks, rotate it immediately.

1.2 Graceful key rotation

Laravel 12 lets you list old keys in APP_PREVIOUS_KEYS so legacy data continues to decrypt while new data uses the fresh key:

APP_KEY="base64:NEW_KEY_HERE"
APP_PREVIOUS_KEYS="base64:OLD_KEY1,base64:OLD_KEY2"

Laravel tries the current key first, then each previous key in order.

2 · Encrypting Individual Values

2.1 Crypt facade & global helpers

When you just need to protect a string or small blob:

use Illuminate\Support\Facades\Crypt;

// Facade
$token = $request->input('token');
$encrypted = Crypt::encryptString($token);
$plain     = Crypt::decryptString($encrypted);

// Global helpers (identical behaviour)
$encrypted = encrypt($token);
$plain     = decrypt($encrypted);

Everything is AES‑encrypted and MAC‑signed under the hood.

2.2 Fluent encryption on Str (Laravel 12.18+)

You can now chain encryption directly on a Str instance:

use Illuminate\Support\Str;

$encrypted = Str::of('super‑secret')->encrypt();
$plain     = Str::of($encrypted)->decrypt();

Great for one‑liners and pipelines.

3 · Model‑Level Encryption with Casts

3.1 Built‑in encrypted casts

Attribute casting keeps your models tidy while Laravel handles encryption transparently:

<?php
// app/Models/Article.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Enums\ArticleStatus;

class Article extends Model
{
    protected $fillable = ['content', 'status', 'published_at'];

    protected $casts = [
        'content'       => 'array',                // JSON → array
        'status'        => ArticleStatus::class,   // Enum cast
        'published_at'  => 'encrypted',            // Encrypted datetime
    ];
}

Other variations: encrypted:array, encrypted:object, encrypted:collection.

3.2 Custom casts & attributes

Need a different cipher, or want to mix encryption with other logic? Create a custom cast:

php artisan make:cast EncryptAsHex

Implement get / set and call Crypt::encryptString internally, or use Attribute::encryptUsing() inside an accessor.

4 · Protecting Environment Files

php artisan env:encrypt               # creates .env.encrypted and prints key
php artisan env:decrypt --key=...     # reverses the process

Options like --cipher and --force let you tweak the workflow for multi‑env setups.

5 · Cookies Are Already Encrypted

Every cookie generated by Laravel is encrypted and signed by the EncryptCookies middleware. Read them with $request->cookie('name'); there’s nothing extra to do.

6 · Client‑Side Encryption (Optional)

Certain regulations require that data leave the browser encrypted. A typical pattern:

  1. Use the Web Crypto API (crypto.subtle.encrypt) in your Vue/React SPA.

  2. Send the ciphertext to the server; Laravel stores it verbatim.

  3. Decrypt in a secure back‑office service (or on demand) with the shared key.

Because keys must live in the browser, evaluate the threat model carefully.

7 · Testing Encrypted Data

public function test_published_at_is_encrypted()
{
    $article = Article::factory()->create([
        'published_at' => encrypt(now()),
    ]);

    $this->assertNotEquals(
        now()->toDateTimeString(),
        $article->getRawOriginal('published_at')
    );

    $this->assertEquals(
        now()->toDateTimeString(),
        decrypt($article->published_at)
    );
}

8 · Security Checklist 🔒

✔️ Step

🛡️ Why It Matters

🔧 Action

🔐 Key hygiene

A leaked key breaks all encryption

Store APP_KEY in a vault; never commit it

🔄 Key rotation

Limits the blast radius of a leak

Use APP_PREVIOUS_KEYS and rotate regularly

🗄️ Encrypt at rest

Protects database dumps & backups

Apply encrypted casts to sensitive columns

📁 Secure env files

Stops config secrets from leaking

env:encrypt in CI; store the decryption key safely

🍪 Encrypted cookies

Prevents client‑side tampering

Rely on default EncryptCookies middleware

🧪 Write tests

Guards against accidental plaintext

Assert raw DB values ≠ decrypted values

🗑️ Zero discard

Remove plaintext logs, debug dumps

Use Log::mask() and disable debug output in production

9 · Takeaways

Laravel 12 offers multiple layers of encryption:

  • Quick secretsCrypt facade / encrypt() helper

  • Fluent pipesStr::encrypt() / decrypt()

  • Column‑levelencrypted casts

  • Config secretsenv:encrypt command

  • Transport → HTTPS (TLS)

Combine these with disciplined key management and you’ll keep user data safe—even if an attacker gets a database dump or environment file.