Подтверждение паролем чувствительных действий в Laravel

Источник: «Password confirmation for sensitive actions on Laravel»
Как легко предотвратить уязвимые действия в вашем Laravel приложении.

Недавно я написал статью о том, как в Laravel можно легко контролировать сессии браузера. Поводом для этой статьи послужил личный неудачный опыт кражи моего мобильного телефона. Чтобы предотвратить доступ и возможность выполнения действий другими людьми, владеющими устройством.

Но мне потребовалось некоторое время, чтобы получить доступ к своим учётным записям с другого устройства и отключить сессии. Размышляя о том, как предотвратить выполнение конфиденциальных действий другими людьми, я вспомнил, что многие используемые мною сервисы время от времени запрашивают пароль для выполнения определённых действий, например:

Неудивительно, что в Laravel есть функция, позволяющая решить эту проблему довольно простым способом:

Защита маршрутов

Прежде всего, необходимо знать, как защитить маршруты, ведущие пользователя к выполнению конфиденциальных действий.

Добавьте встроенное middleware \Illuminate\Auth\Middleware\RequirePassword::class, которое определяется в файле app/Http/Kernel.php, имея в качестве псевдонима password.confirm.

Допустим, вы хотите запретить пользователю отключать другие устройства, на которых он ранее вошёл в систему.

app/routes/web.php:

Route::get('/disconnect-other-devices', function () {
// ...
})->middleware(['auth', 'password.confirm']);

Как видно, middleware password.confirm установлено. Оно проверяет, не истёк ли срок действия элемента сессии auth.password_confirmed_at, основываясь на значении конфигурации auth.password_timeout. По умолчанию это значение равно трём часам.

Время жизни сессии в Laravel по умолчанию составляет два часа. Если оставить эти значения, то middleware password.confirm будет работать только с теми сессиями пользователей, которые были настроены на запоминание. Конечно, можно увеличить время жизни сессии, и тогда подтверждение пароля будет работать для всех случаев сессии.

По умолчанию middleware перенаправляет пользователя на именованный маршрут password.confirm. Как я объясню в следующем разделе, вы можете изменить имя маршрута.

Если в запросе ожидается ответ в формате JSON, то вместо перенаправления выдаётся сообщение об ошибке. Вот как это обрабатывает middleware:

\Illuminate\Auth\Middleware\RequirePassword::class:

//...
if ($request->expectsJson()) {
return $this->responseFactory->json([
'message' => 'Password confirmation required.',
], 423);
}

return $this->responseFactory->redirectGuest(
$this->urlGenerator->route($redirectToRoute ?: 'password.confirm')
);
//...

Форма подтверждения пароля

Если запрос не ожидает ответа в формате JSON, то можно вывести форму, позволяющую ввести пароль. По умолчанию маршрут должен иметь имя password.confirm.

app/routes/web.php:

Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('password.confirm');

Вы можете изменить его, передав имя маршрута при настройке middleware:

app/routes/web.php:

Route::get('/disconnect-other-devices', function () {
// ...
})->middleware(['auth', 'password.confirm:my-custom-named-route']);

Route::get('/confirm-password', function () {
return view('auth.confirm-password');
})->middleware('auth')->name('my-custom-named-route');

Подтверждение пароля

Для его подтверждения необходимо выполнить валидацию и вызвать метод passwordConfirmed, как показано ниже:

if (! Hash::check($request->password, $request->user()->password)) {
return back()->withErrors([
'password' => ['The provided password does not match our records.']
]);
}

$request->session()->passwordConfirmed();

return redirect()->intended();

Если выполняется Ajax-запрос:

if (! Hash::check($request->password, $request->user()->password)) {
return response()->json([
'message' => 'The provided password does not match our records',
], 422);
}

$request->session()->passwordConfirmed();

return response()->noContent();

В заключении

В этом посте вы узнали, как защитить конфиденциальные действия в приложениях Laravel.

Если у вас есть комментарии, вы можете поделиться ими в обсуждении в Twitter).

Дополнительные материалы

Предыдущая Статья

Как более эффективно прослушивать событие в разных сценариях

Следующая Статья

Четыре способа быстрого добавления CSS на Laravel сайт