Strengthen the password requirements of your Laravel application with minimal effort
An easy security boost within minutes, for free
During a conversation with a client on the eve of their application rollout to the rest of the organization, the topic of user account security came up. We reflected on the idea that accounts are only as secure as the measures taken by users (choosing strong passwords, enabling two-factor), and the decision on whether to enforce these measures. When I suggested boosting the password requirements to be more strict instead of relying on users to choose strong passwords, it was a zero-hesitation decision with a high perceived value by the business-minded client.
I was excited to finally make use of the more advanced password rules Laravel has to offer, which up to that point I had only read about here and there. But then I thought, "Shouldn't I be doing this in all projects, to begin with?"
The quick version
Before deploying your application, take a moment to update the default password rules to at least something stronger than the defaults. Do this by customizing the application-wide password rules inside App\Actions\Fortify\PasswordValidationRules
, and leverage the convenient pre-built validation rules provided by Laravel's Password
rule object.
The detailed version
If you're working in an application backed by Fortify, the password rules are in the App\Actions\Fortify\PasswordValidationRules
trait, which you are free to customize. If you are not using Fortify, keep reading, you will still be able to implement the principles demonstrated here. At the time of this writing (Laravel 9.x), this is the default content of the trait:
<?php
namespace App\Actions\Fortify;
use Laravel\Fortify\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', new Password, 'confirmed'];
}
}
By default, the ruleset includes an instance of Fortify's Password rule object (Laravel\Fortify\Rules\Password
) which provides a few customizations:
(new Password)
->length(8) // minimum length of the password
->requireUppercase() // at least one uppercase character required
->requireNumeric() // at least one numeric digit required
->requireSpecialCharacter(); // at least one special character required
You may also swap it out with Laravel's native Password rule object (Illuminate\Validation\Rules\Password
) that offers more:
Password::min(8) // minimum size of the password
->mixedCase() // require at least one uppercase and one lowercase letter
->numbers() // require at least one number
->symbols() // require at least one symbol
->letters() // require at least one letter
->uncompromised(threshold: 0); // password has not been compromised in data leaks
The two rule objects offer mostly the same capabilities, except for uncompromised()
only available in the latter.
Putting it all together, here's what your application's customized PasswordValidationRules
trait might look like:
<?php
namespace App\Actions\Fortify;
use Illuminate\Validation\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array
*/
protected function passwordRules()
{
return [
'required',
'string',
Password::min(8)
->mixedCase()
->numbers()
->symbols()
->letters()
->uncompromised(),
'confirmed',
];
}
}
This is such an effortless update to make thanks to the framework, yet it's easy to overlook.
Not using Fortify
If authentication is not powered by Fortify (e.g., Laravel Breeze), then the core principles above are still applicable wherever password creation and updates are being validated. You may instead establish your application's custom password rules via Password::defaults()
:
// In the boot() method of AuthServiceProvider:
Password::defaults(
fn () => Password::min(8)
->uncompromised()
);
Then wherever password rules are to be applied, simply use Password::defaults()
. Laravel Breeze, for example, already does this.
Provide the end user with good messaging
After you customize the password rules, be sure to go the extra mile by also offering appropriate messaging on frontend forms wherever a password gets chosen or updated (e.g., profile update, user registration). In the following sample registration page, I've added secondary text underneath the Password field informing the user of the password expectations.
Choose the wording as you wish, as long as you offer something for the user to know the password requirements ahead of time. This way, you avoid them having to find out through potentially repeated validation errors, which will be a frustrating user experience.
In conclusion
When building applications for clients, being able to offer this kind of boost in security will be of huge perceived value to them from a non-developer perspective (freelancers, take note). I've been doing this long enough to know how annoying it would be to implement the above manually during my days before Laravel (or frameworks in general). It's one of the reasons I've come to love working with Laravel.