Search code examples
laravelormbreezecrypt

Issue with Crypt Facades on user email in forgot password feature Laravel 11 Breeze Package


I'm using Laravel 11 with the Breeze package for authentication. In my application, I've encrypted user data like email and name using the Crypt Facades.

The registration process works as expected. However, after logout, when attempting to login again, I encounter an error stating "Email not found".

I suspect the issue arises because the email field contains encrypted data, and the logic for forgot password compares the input with the original value.

Crypt facade used "AES-256-CBC" encryption.

Here's a simplified example:

$email = '[email protected]'

I need to achieve a query result similar to the following:

select * from users where DECRYPT(email)=$email

My concern is that if I encrypt the $email and compare it to the database email, the generated string after encryption will always be different, resulting in null.

Has anyone encountered a similar scenario or have ideas on how to address this without resorting to multiple loops?

Thank you for any assistance or suggestions!

I tried with below.

use App\Models\User;
use Illuminate\Support\Facades\Crypt;

// Decrypt the email you want to search for
$decryptedEmail = '[email protected]';

// Retrieve all users from the database
$users = User::all();

// Filter users whose decrypted email matches the desired email
$matchingUsers = $users->filter(function ($user) use ($decryptedEmail) {
    return Crypt::decryptString($user->email) === $decryptedEmail;
});

I want to achieve without looping.


Solution

  • To achieve this without looping, you need to store a hashed version of the email in a separate column that you can use for querying. This way, you can use the hash of the input email to find the user without needing to decrypt each email stored in the database.

    Add a Hashed Email Column: First, add a new column to your users table to store the hashed version of the email.

    php artisan make:migration add_email_hash_to_users_table --table=users
    

    In the migration file:

    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('email_hash')->index();
        });
    }
    
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('email_hash');
        });
    }
    

    Update Registration and Email Change Logic: When registering a new user or updating an email, store the hash of the email.

    use Illuminate\Support\Facades\Hash;
    use Illuminate\Support\Facades\Crypt;
    
    $user = new User;
    $user->name = 'Pragna';
    $user->email = Crypt::encryptString('[email protected]');
    $user->email_hash = Hash::make('[email protected]');
    $user->save();
    

    Update Registration and Email Change Logic: When registering a new user or updating an email, store the hash of the email.

    use Illuminate\Support\Facades\Hash;
    use App\Models\User;
    
    $email = '[email protected]';
    $hashedEmail = Hash::make($email);
    
    // Find the user by the hashed email
    $user = User::where('email_hash', $hashedEmail)->first();
    
    if ($user && Crypt::decryptString($user->email) === $email) {
        // Email matches, proceed with login
    } else {
        // Email not found or doesn't match
    }
    

    However, Hash::make generates a unique hash each time it's called, so for searching purposes, use a consistent hashing algorithm like SHA-256:

    use Illuminate\Support\Facades\Crypt;
    use Illuminate\Support\Facades\DB;
    
    // Hash the email using SHA-256
    $hashedEmail = hash('sha256', '[email protected]');
    
    // Find the user by the hashed email
    $user = DB::table('users')->where('email_hash', $hashedEmail)->first();
    
    if ($user && Crypt::decryptString($user->email) === '[email protected]') {
        // Email matches, proceed with login
    } else {
        // Email not found or doesn't match
    }
    

    Update Existing Users: If you have existing users, you need to update their email_hash column. You can do this via a Laravel command or a database script.

    use Illuminate\Console\Command;
    use App\Models\User;
    use Illuminate\Support\Facades\Hash;
    
    class UpdateEmailHashes extends Command
    {
        protected $signature = 'update:email-hashes';
    
        public function handle()
        {
            $users = User::all();
            foreach ($users as $user) {
                $user->email_hash = hash('sha256', Crypt::decryptString($user->email));
                $user->save();
            }
        }
    }
    

    Run the command:

    php artisan update:email-hashes
    

    Enjoy !