Skip to content

Commit ecb05f5

Browse files
authored
Merge pull request #3862 from Laravel-Backpack/throttle-password-request
Throttle password request attempts
2 parents 63b7d8a + d50ae0f commit ecb05f5

File tree

8 files changed

+71
-8
lines changed

8 files changed

+71
-8
lines changed

src/BackpackServiceProvider.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Backpack\CRUD;
44

5+
use Backpack\CRUD\app\Http\Middleware\ThrottlePasswordRecovery;
56
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
67
use Illuminate\Routing\Router;
78
use Illuminate\Support\Collection;
@@ -93,6 +94,12 @@ public function registerMiddlewareGroup(Router $router)
9394
foreach ($middleware_class as $middleware_class) {
9495
$router->pushMiddlewareToGroup($middleware_key, $middleware_class);
9596
}
97+
98+
// register internal backpack middleware for throttling the password recovery functionality
99+
// but only if functionality is enabled by developer in config
100+
if (config('backpack.base.setup_password_recovery_routes')) {
101+
$router->aliasMiddleware('backpack.throttle.password.recovery', ThrottlePasswordRecovery::class);
102+
}
96103
}
97104

98105
public function publishFiles()
@@ -268,7 +275,8 @@ public function loadConfigs()
268275
'backpack' => [
269276
'provider' => 'backpack',
270277
'table' => 'password_resets',
271-
'expire' => 60,
278+
'expire' => 60,
279+
'throttle' => config('backpack.base.password_recovery_throttle_notifications'),
272280
],
273281
];
274282

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\app\Http\Middleware;
4+
5+
use Illuminate\Routing\Middleware\ThrottleRequests;
6+
use Illuminate\Validation\ValidationException;
7+
8+
class ThrottlePasswordRecovery extends ThrottleRequests
9+
{
10+
/**
11+
* Return a validation exception with a nice message to the user instead of the big fat app error.
12+
*
13+
* @param \Illuminate\Http\Request $request
14+
* @param string $key
15+
* @param int $maxAttempts
16+
* @param callable|null $responseCallback
17+
* @return \Illuminate\Validation\ValidationException
18+
*/
19+
protected function buildException($request, $key, $maxAttempts, $responseCallback = null)
20+
{
21+
return ValidationException::withMessages([
22+
'email' => [trans('backpack::base.throttled_request')],
23+
]);
24+
}
25+
}

src/app/Library/Auth/PasswordBroker.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
*/
1313
class PasswordBroker extends OriginalPasswordBroker
1414
{
15+
public const RESET_THROTTLED = 'backpack::base.throttled';
16+
1517
/**
1618
* Send a password reset link to a user.
1719
*
@@ -29,7 +31,8 @@ public function sendResetLink(array $credentials, Closure $callback = null)
2931
return static::INVALID_USER;
3032
}
3133

32-
if ($this->tokens->recentlyCreatedToken($user)) {
34+
if (method_exists($this->tokens, 'recentlyCreatedToken') &&
35+
$this->tokens->recentlyCreatedToken($user)) {
3336
return static::RESET_THROTTLED;
3437
}
3538

src/config/backpack/base.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,6 @@
193193
// Warning: if you disable this, the password recovery routes (below) will be disabled too!
194194
'setup_auth_routes' => true,
195195

196-
// Set this to false if you would like to skip adding the password recovery routes
197-
// (you then need to manually define the routes in your web.php)
198-
'setup_password_recovery_routes' => true,
199-
200196
// Set this to false if you would like to skip adding the dashboard routes
201197
// (you then need to overwrite the login route on your AuthController)
202198
'setup_dashboard_routes' => true,
@@ -205,6 +201,32 @@
205201
// (you then need to manually define the routes in your web.php)
206202
'setup_my_account_routes' => true,
207203

204+
// Set this to false if you would like to skip adding the password recovery routes
205+
// (you then need to manually define the routes in your web.php)
206+
'setup_password_recovery_routes' => true,
207+
208+
/*
209+
|--------------------------------------------------------------------------
210+
| Security
211+
|--------------------------------------------------------------------------
212+
*/
213+
214+
// Backpack will prevent visitors from requesting password recovery too many times
215+
// for a certain email, to make sure they cannot be spammed that way.
216+
// How many seconds should a visitor wait, after they've requested a
217+
// password reset, before they can try again for the same email?
218+
'password_recovery_throttle_notifications' => 600, // time in seconds
219+
220+
// Backpack will prevent an IP from trying to reset the password too many times,
221+
// so that a malicious actor cannot try too many emails, too see if they have
222+
// accounts or to increase the AWS/SendGrid/etc bill.
223+
//
224+
// How many times in any given time period should the user be allowed to
225+
// attempt a password reset? Take into account that user might wrongly
226+
// type an email at first, so at least allow one more try.
227+
// Defaults to 3,10 - 3 times in 10 minutes.
228+
'password_recovery_throttle_access' => '3,10',
229+
208230
/*
209231
|--------------------------------------------------------------------------
210232
| Authentication

src/resources/lang/en/base.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,7 @@
6666
'confirm_email' => 'Confirm Email',
6767
'choose_new_password' => 'Choose New Password',
6868
'confirm_new_password' => 'Confirm new password',
69+
'throttled' => 'You have already requested a password reset recently. Please check your email. If you do not receive our email, please retry later.',
70+
'throttled_request' => 'You have exceeded the limit of tries. Please wait a few minutes and try again.',
6971

7072
];

src/resources/lang/pt/base.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,6 @@
6666
'confirm_email' => 'Confirmar Email',
6767
'choose_new_password' => 'Escolher nova password',
6868
'confirm_new_password' => 'Confirmar nova password',
69-
69+
'throttled' => 'Efetuou um pedido de recuperação de password recentemente. Verifique o seu email. Caso não tenha recebido o email tente novamente dentro de alguns instantes.',
70+
'throttled_request' => 'Excedeu o limite de tentativas. Aguarde alguns minutos e tente novamente.',
7071
];

src/resources/lang/ro/base.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,6 @@
5757
'button' => 'Schimbă parola',
5858
'notice' => 'Dacă nu ați facut dvs cererea, nu este nevoie să faceți nimic, parola nu va fi schimbată.',
5959
],
60+
'throttled' => 'Ați cerut deja schimbarea parolei, de curând. Verificați email-ul primit pentru instrucțiuni. Dacă nu primiți email-ul, vă rugăm să încercați mai târziu.',
61+
'throttled_request' => 'Ați depășit limita de încercări. Așteptați câteva minute, apoi încercați din nou.',
6062
];

src/routes/backpack/base.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function () {
3434
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('backpack.auth.password.reset');
3535
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
3636
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('backpack.auth.password.reset.token');
37-
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('backpack.auth.password.email');
37+
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('backpack.auth.password.email')->middleware('backpack.throttle.password.recovery:'.config('backpack.base.password_recovery_throttle_access'));
3838
}
3939
}
4040

0 commit comments

Comments
 (0)