Security Tip: What If You Hashed Null?

[Tip #100] One of the fun parts of doing my security audits is coming across unexpected code that looks exploitable, and trying it out myself to see what possibilities exist.

Security Tip: What If You Hashed Null?

One of the fun parts of doing my security audits is coming across unexpected code that looks exploitable, and trying it out myself to see what possibilities exist. So today we're going to explore a recent example I found, and have some fun thinking about the What Ifs... πŸ€”

Consider code:

public function update(Request $request): RedirectResponse
{
    $validated = $request->validateWithBag('updatePassword', [
        'current_password' => ['required', 'current_password'],
        'password' => ['nullable', Password::defaults(), 'confirmed'],
    ]);

    $request->user()->update([
        'password' => Hash::make($validated['password'] ?? null),
    ]);

    return back()->with('status', 'password-updated');
}

\App\Http\Controllers\Auth\PasswordController::update

Specifically this line:

'password' => Hash::make($validated['password'] ?? null),

It got me thinking:

What happens if you try and hash a null password?

Let's find out!

> Hash::make(null);

   DEPRECATED  password_hash(): Passing null to parameter #1 ($password) of type string is deprecated in vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php on line 49.

= "$2y$12$gIH.P1oapLUjQNDnmfMXKeU6QZikV.EL.dX6LJBTtYCX7rCUuSVmq"

If we ignore the deprecation warning for now, we can see null does indeed get successfully hashed. Following a hunch, let's see if the code has magically turned that code into an empty string:

> $password = '$2y$12$gIH.P1oapLUjQNDnmfMXKeU6QZikV.EL.dX6LJBTtYCX7rCUuSVmq';

> Hash::check('', $password);
= true

Yes, hashing null matches an empty string! 😈

This gave me the idea to check if the app accepted an empty password on login - my theory being that you could log into accounts where a null password was set without needing to provide any password. I went digging through the app, and the framework, looking for how passwords were handled, trying to find a configuration that would allow this to be exploited...

To cut a long story short, the answer was a resounding No, on multiple levels. 😭

To start with, the app had a required validation rule on the login form password field - which is Laravel's default. Plus the ConvertEmptyStringsToNull middleware transformed it to a null, which Laravel rejects inside the password helpers.

So if I couldn't exploit it, why am I telling you this?

Even though I couldn't exploit it, I still find this code demonstrates one of the fundamentals of security that I strongly believe in: Asking What If?

As developers, we're very good at writing code that solves a specific problem, but we're also very good at assuming code does what we expect it to. So many vulnerabilities come about from weird edge cases, so we need to be constantly asking: What If?

πŸ’‘
You'll note the deprecation warning thrown when passing null into the password_hash() method. This feels like a known issue that's been exploited in the wild before, so it's a good thing it'll be impossible soon! 🀞

If you found this security tip useful, subscribe to get weekly Security Tips straight to your inbox. Upgrade to a premium subscription for exclusive monthly In Depth articles, or drop a coin in the tip jar to show your support.

Looking for a Laravel Security Audit / Penetration Test, or a budget-friendly Security Review? Feel free to reach out! You can also connect with me on Bluesky, or other socials. And don’t miss Practical Laravel Security, my interactive course designed to boost your Laravel security skills.