Подтверждение паролем чувствительных действий в 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).