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:
- Decrypt all data using the old key
 - Change the key
 - 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
- ✅ Only encrypt fields that require it
 - 🔐 Never store encryption keys in version control
 - 📁 Use environment variables for key management
 - 🧪 Always test decryption before deployment
 - 🕵️ Avoid logging decrypted sensitive values
 - 📤 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