How to Encrypt Database Fields in Laravel 12 Using Casting

Data security has become a non-negotiable aspect of modern web development. Whether you’re handling sensitive user information, healthcare records, or payment data, ensuring that this information is encrypted before storing it in your database is a must. Laravel 12, one of the most powerful PHP frameworks, offers an elegant and efficient way to handle encryption using Eloquent Attribute Casting.

In this blog post, we will take a deep dive into how to encrypt and decrypt database fields in Laravel 12 using custom casting. By the end of this tutorial, you will have a solid understanding of how to store encrypted values securely, retrieve them conveniently, and follow best practices to protect sensitive data at rest.


📌 What is Attribute Casting in Laravel?

Before jumping into encryption, let’s quickly revisit what casting means in Laravel.

Casting allows you to convert attributes to a specific data type when retrieving or setting them on an Eloquent model. For example, if you cast a birthday attribute to date, Laravel will automatically convert it to a Carbon instance.

Laravel supports basic cast types like:

protected $casts = [
    'is_admin' => 'boolean',
    'created_at' => 'datetime',
];

But starting with Laravel 7 and continuing in Laravel 12, you can define your own custom casts, which opens the door to advanced use cases — like encrypting fields.


🔐 Why Use Casting for Encryption?

Traditionally, developers used mutators and accessors to handle encryption. However, this approach can become repetitive and cluttered if you need to encrypt multiple fields across different models.

Custom attribute casting in Laravel 12 provides a cleaner and reusable approach to encrypting and decrypting model fields, making your code more modular and maintainable.

Benefits of Using Casting for Encryption:

  • Reusable across any model
  • Cleaner and more readable syntax
  • Centralized encryption logic
  • Easy to extend or modify
  • Keeps business logic separate from data handling

🏗️ Setting Up Laravel 12 Project

Let’s walk through a practical example of encrypting the phone_number and ssn (Social Security Number) fields in a User model.

Step 1: Create a Laravel Project (if you don’t have one)

composer create-project laravel/laravel laravel-encryption
cd laravel-encryption

Step 2: Set Up the Database

Update your .env file with the correct database configuration:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_encryption
DB_USERNAME=root
DB_PASSWORD=

Then create the database in MySQL or your preferred database.


📦 Step-by-Step: Encrypting Fields with Custom Casts

Step 1: Add Fields to the Users Table

Run the migration to modify the users table:

php artisan make:migration add_sensitive_fields_to_users_table --table=users

In the migration file:

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->text('phone_number')->nullable();
        $table->text('ssn')->nullable();
    });
}

Run the migration:

php artisan migrate

Step 2: Create a Custom Cast Class

Run the artisan command:

php artisan make:cast Encrypted

This will create a file at app/Casts/Encrypted.php.

Edit it as follows:

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Database\Eloquent\SerializesCastableAttributes;

class Encrypted implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
        try {
            return $value ? Crypt::decrypt($value) : null;
        } catch (\Exception $e) {
            return null; // You can also log this if needed
        }
    }

    public function set($model, string $key, $value, array $attributes)
    {
        return $value ? Crypt::encrypt($value) : null;
    }
}

This class tells Laravel how to set (encrypt) and get (decrypt) the field value.


Step 3: Update the Model to Use the Cast

Open your User.php model and use the new cast:

use App\Casts\Encrypted;

class User extends Authenticatable
{
    protected $fillable = [
        'name', 'email', 'phone_number', 'ssn'
    ];

    protected $casts = [
        'phone_number' => Encrypted::class,
        'ssn' => Encrypted::class,
    ];
}

Now Laravel will automatically encrypt the values before inserting them into the database and decrypt them when retrieving from the database.


✍️ Example Usage

Let’s test this feature in a controller or Tinker.

Saving Encrypted Data

User::create([
    'name' => 'Jane Doe',
    'email' => '[email protected]',
    'phone_number' => '9876543210',
    'ssn' => '123-45-6789',
]);

Retrieving Data

$user = User::where('email', '[email protected]')->first();

echo $user->phone_number; // 9876543210
echo $user->ssn;          // 123-45-6789

Database View

In your database, phone_number and ssn will be stored as encrypted strings like:

eyJpdiI6IkZ0WGR3QU01QkFHZUtkNzQ...

This makes them unreadable without Laravel’s encryption key.


🧪 Handling Edge Cases and Errors

It’s important to ensure that your custom cast is robust. In our earlier Encrypted class, we wrapped the decryption inside a try-catch block:

try {
    return $value ? Crypt::decrypt($value) : null;
} catch (\Exception $e) {
    return null;
}

This prevents your application from crashing in case:

  • The field contains a corrupted or non-encrypted string
  • The encryption key has changed
  • The value is empty

🔍 Important Considerations

While encrypting fields is great for security, it comes with a few limitations:

1. You Can’t Query Encrypted Fields Directly

Because the value is encrypted, this won’t work:

User::where('phone_number', '9876543210')->first(); // ❌

✅ Workaround: Use a Hash Field

Add a phone_number_hash field and save a hash alongside the encrypted value.

In User.php:

public function setPhoneNumberAttribute($value)
{
    $this->attributes['phone_number'] = Crypt::encrypt($value);
    $this->attributes['phone_number_hash'] = hash('sha256', $value);
}

Now you can search like:

$hash = hash('sha256', '9876543210');
$user = User::where('phone_number_hash', $hash)->first();

2. Sorting and Indexing Won’t Work

Because encrypted values are long, random strings, you can’t sort or index them in a meaningful way.


🔐 Rotating the Encryption Key

If you change the APP_KEY, previously encrypted data becomes unreadable. Laravel does not support automatic key rotation.

To rotate keys securely:

  1. Decrypt all data using the old key
  2. Change the key
  3. Re-encrypt the data using the new key

This should be done with caution and preferably during a maintenance window.


🔄 Encrypting Arrays or JSON

Laravel’s Crypt::encrypt() works with arrays too:

$encrypted = Crypt::encrypt(['foo' => 'bar']);
$data = Crypt::decrypt($encrypted);

This is useful for storing encrypted preferences or metadata fields.


🛡 Best Practices for Field Encryption

  1. Only encrypt fields that require it
  2. 🔐 Never store encryption keys in version control
  3. 📁 Use environment variables for key management
  4. 🧪 Always test decryption before deployment
  5. 🕵️ Avoid logging decrypted sensitive values
  6. 📤 Ensure backups are encrypted, as field encryption does not protect database dumps

💡 When to Use Field Encryption

Field encryption is ideal when:

  • You need to store personally identifiable information (PII)
  • Your app is subject to HIPAA, GDPR, or PCI-DSS
  • You want to add another layer of defense in case of a data breach

🧾 Conclusion

With Laravel 12’s support for custom casting, encrypting database fields has become not only more secure but also more elegant. By using attribute casting, you can create reusable, clean, and centralized logic for protecting sensitive data in your application.

Encrypting fields such as phone numbers, social security numbers, bank details, or any PII ensures that even if your database is compromised, the data remains useless without the decryption key.

Laravel 12 gives you the tools — it’s up to you to use them wisely. 🛡️


👨‍💻 Ready to Encrypt?

Whether you’re building a Laravel-based healthcare, fintech, or enterprise app, it’s time to embrace field-level encryption.

Have questions or want help implementing this in your project? Drop a comment below or reach out to us. Happy coding! 🚀


Let me know if you’d like this exported in Markdown or Word format for easy blog publishing.


Leave a Reply

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