langleyfoxall / laravel-nist-password-rules

🔒 Laravel validation rules that follow the password related recommendations found in NIST Special Publication 800-63B section 5.
GNU Lesser General Public License v3.0
208 stars 49 forks source link

Implement Rate Limiting (Throttling) #11

Open DivineOmega opened 5 years ago

DivineOmega commented 5 years ago

We should attempt to implement login rate limiting as part of these validation rules, as described in NIST SP800-63b section 5.2.2.

the verifier SHALL implement controls to protect against online guessing attacks. the verifier SHALL limit consecutive failed authentication attempts on a single account to no more than 100. Requiring the claimant to wait following a failed attempt for a period of time that increases as the account approaches its maximum allowance for consecutive failed attempts (e.g., 30 seconds up to an hour).

Source: https://pages.nist.gov/800-63-3/sp800-63b.html#throttle

It would also be useful to provide Artisan commands that will remove login bans / delays entirely or for specific users / IPs.

HDVinnie commented 5 years ago

Would this really be needed since Laravel already offers throttling? https://laravel.com/docs/5.7/authentication#login-throttling

Defaults can also easily be overridden by using such in the Controller.

    // Max Attempts Until Lockout
    public $maxAttempts = 5;

    // Minutes Lockout
    public $decayMinutes = 60;
DivineOmega commented 5 years ago

You're right. Most of this is covered by Laravel's build in login throttling.

It's not exactly as specified by NIST though. For example, it does not support...

a period of time that increases as the account approaches its maximum allowance for consecutive failed attempts (e.g., 30 seconds up to an hour

Also it does not provide the ability to fully lock out an account after a number of attempts, as in the following.

the verifier SHALL limit consecutive failed authentication attempts on a single account to no more than 100.

HDVinnie commented 5 years ago

Makes sense. Good idea!

DivineOmega commented 5 years ago

Update: see the following comment.

To make integration as seamless as possible, this functionality would require the validation rules to have knowledge of how to check if a user's credentials are valid. This could be achieved via a callback, as follows.

$hasValidCredentials = function(Request $request) {
    return Auth::attempt(['email' => $request->email, 'password' => $request->password], false, false);
};

$this->validate($request, [
    'email' => 'required',
    'password' => PasswordRules::login($hasValidCredentails),
]);
DivineOmega commented 5 years ago

Actually, the Rule object does not have direct access to the Request, so it may be best if this is checked outside of the rule and a boolean is passed in, as follows.

$hasValidCredentials = Auth::attempt([
    'email' => $request->email, 
    'password' => $request->password,
], false, false);

$this->validate($request, [
    'email' => 'required',
    'password' => PasswordRules::login($hasValidCredentails),
]);