К основному контенту

Подписанные маршруты (роуты) в Laravel


Начиная с версии 5.6.12 Laravel обзавелся полезным функционалом «подписи» маршрутов. Так как я тяготею к криптографии я решила углубленно разобраться с этой фичей и рассказать как ею пользоваться.
Посмотрим что гласит официальная документация:
"Подписанные URL обладают хэшом «подписи», добавленным к строке запроса, что позволяет Laravel проверять, не был ли URL изменен с момента его создания. Подписанные URL-адреса особенно полезны для маршрутов, которые являются общедоступными, но требуют уровня защиты от манипулирования URL-адресами."

Итак, статья будет состоять из таких частей:
  • Зачем?
  • Как пользоваться?
  • Как работает?
Примеры использования: 
  1. Вам необходимо отправить письмо пользователю с простым вопросом и ответом да/нет. Например, пойдет ли он на мероприятие. Если пользователь не авторизован на сайте - ему придется входить в систему, и скорее всего – он этого не сделает и вы останетесь без ответа;
  2. Вам необходимо подтвердить почтовый ящик пользователя. Задача та же – необходимо чтобы без авторизации он смог подтвердить данные. 
Здесь нам придут на помощь подписанные маршруты. Пользователю не придется входить в систему чтобы перейти по ссылке, но ссылка может достоверно идентифицировать переданную информацию, а именно то, что она не была изменена.
Кстати, в Laravel 5.7 на базе подписанных маршрутов появился встроенный функционал подтверждения ящика электронной почты пользователя.

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

Убедитесь, что в Http/Kernel.php у вас есть посредник signed: 

protected $routeMiddleware = [
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];


Создаем такой маршрут.

use Illuminate\Http\Request;

Route::get('email/{email}/{user}', function ($email, $user) {
    if (! $request->hasValidSignature()) {
        abort(401);
    }
    // Action
})->name('changeEmail')->middleware('signed');


В данном случае нам необходима будет информация о новом email и id пользователя для внесения данных об измененном ящике в базу. Маршрут и параметры могут быть любыми. Главное добавить на него посредника signed и имя. 
Чтобы обратиться к нашему маршруту сгенерируем ссылку с данными: 

use \Illuminate\Support\Facades\URL;
$url = URL::temporarySignedRoute('changeEmail', now()->addHour(), [ 
    'email' => 'test@test.com',
    'user' => 1 
]);

Эту ссылку мы и поместим в письмо с запросом на подтверждение ящика. В данном случае мы создали временно действующую ссылку (1 час) использовав temporarySignedRoute. 

Laravel сгенерирует ссылку вида: 


Теперь, используя данную ссылку, если какие-то данные в ней изменятся (id пользователя, или какой-то символ из подписи, почтового ящика) Laravel выбросит исключение Illuminate\Routing\Exceptions\InvalidSignatureException. Это защищает приложение от грубого перебора и нарушения целостности данных. 

Если нет необходимости ограничивать время действия ссылки, то просто используйте signedRoute. 

$url = URL::signedRoute 
('changeEmail', [ 
    'email' => 'test@test.com',
    'user' => 1 
]);

А теперь раздел для самых любопытных или интересующихся криптосоставляющей вопроса.
Собственно, самой наглядной функцией всей этой системы является \Illuminate\Routing\UrlGenerator::signedRoute.

Из чего состоит подпись?
Это HMAC хэш (sha256) от самого маршрута (в нашем случае это «http://test.local/email/test@test.com/1» + параметр окончания срока действия ссылки «?expires=1563484975» в формате UNIX timestamp). 
Ключом шифрования является секретный ключ приложения, который мы генерируем при создании приложения командой key:generate и который стоит в .env. 

Проверка подписи происходит повторным взятием от ссылки (http://test.local/email/test@test.com/1?expires=1563484975) HMAC хэша и его сверкой с подписью. 

Вот и вся магия! 

Надеюсь, я смогла доходчиво рассказать о том, как пользоваться подписанными маршрутами и как это работает :)

Источники:
  1. Getting Started with Signed Routes in Laravel https://laravel-news.com/signed-routes
  2. URL Generation https://laravel.com/docs/5.8/urls#signed-urls
  3. HMAC https://ru.wikipedia.org/wiki/HMAC

Комментарии

Популярные сообщения из этого блога

ВОПРОС К ЧИТАТЕЛЯМ

Уважаемые читатели моего блога и просто проходящие мимо! У меня появилась идея заняться созданием уроков по интересующим вас темам. Предпочтительно на C++, но не ограничиваясь ими, я хочу поделиться своими знаниями не только в виде готового кода но и подробными объяснениями что к чему. Прошу вас, если вы имеете идею и хотели бы разобраться в какой-то теме - отпишитесь в комментариях, что было бы вам интересно. Постараюсь помочь. 

7 глава, Лафоре Р

*1. Напишите функцию reversit(), которая переворачивает строку (массив типа char). Используйте цикл for, который меняет местами первый и последний символы, затем следующие и т. д. до предпоследнего. Строка должна пере- даваться в функцию reversit() как аргумент. Напишите программу для выполнения функции reversit(). Программа долж- на принимать строку от пользователя, вызывать функцию reversit(), а за- тем выводить полученный результат. Используйте метод ввода, который позволяет использовать внутренние пробелы. Протестируйте программу на примере фразы «Аргентина манит негра».

6 глава, Лафоре Р

*1. Создайте класс Int, имитирующий стандартный тип int. Единственное поле этого класса должно иметь тип int. Создайте методы, которые будут устанавливать значение поля, равным нулю, инициализировать его целым значением, выводить значение поля на экран и складывать два значения типа Int. Напишите программу, в которой будут созданы три объекта класса Int, два из которых будут инициализированы. Сложите два инициализирован- ных объекта, присвойте результат третьему, а затем отобразите результат на экране.