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

Подписанные маршруты (роуты) в 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

Комментарии

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

Прата С. Решение задач 6 главы

На решение этих задач у меня ушло 2 дня. Становится все интереснее и интереснее! и черт возьми, я наконец-то поняла работу с файлами! прошу прощения за корявые условия. просто в нормальном формате электронной книги у меня нет, потому скрины задач просто пропущены через Файнридер.

Прата С. решение задач 5 главы

Эта глава по циклам меня удивила. я  с неким пренебрежением отнеслась к ней, т.к. думала что и так все знаю по этой теме. но Прата так глубоко и в подробностях объяснил тему, что я заново переосмыслила циклы и выражения. было очень много интересных подробностей, которые мне никто не мог толком объяснить. и кстати, парочка задач была над которыми я поломала голову. особенно понравился такой акцент на указателях. как говорится, приучают с малых лет. понять - поняла, но путаюсь в них до сих пор нечеловечески.

Прата С. Решение 3 и 4 главы

Повторюсь, взяла только интересные мне задания, т.е. с элементами того что поняла плохо/не встречала ранее. Сделала за сегодня. с удовольствием отмечаю что сдвиги  в положительную сторону  хотя-бы в стиле есть . в 4 главе ясно объяснено почему при вводе нескольких строк начинаются проблемы, и как этого избежать, подробно описана работа с входным потоком. Как работать со строками стиля Си и класса String, разница. для себя я сделала выводы что лучше всего, конечно-же класс string. намного меньше мороки. и метод ввода getline в обоих случаях. в задачах разница проиллюстрирована. и кстати, указатели больше не вызывают у меня ужаса и трепета. много нового узнала о видах структур. эта глава расставила все по местам в моей голове. правда, зачем в задачи на СР втиснута работа с массивом array - загадка. он ведь толком не объяснен, но понять его интуитивно можно, правда, зачем его использовать не зная тонкостей, как обычный массив с немного иным объявле...