Как устроен Laravel signed URL
В этой статье хочу немного посвятить в процесс генерации подписанных URL в Laravel и как это можно применять на практике.
Всем привет!
Примерно с версии Laravel 5.6 в этом фреймворке появилась функция подписи параметров URL для предотвращения модификации адреса и подмены параметров query string. Простыми словами при генерации URL, в query string добавляется новый параметр signature
, который содержит некий хеш, который сгенерирован на основе всех остальных параметров и если хоть в одном из параметров значение изменится, то ссылка считается не действительной.
В качестве одного из примеров, о котором рассказывает официальная документация — это генерация ссылки для отписки от, например, рассылок.
use Illuminate\Support\Facades\URL;// http://site.com/unsubscribe?user=1&signature=hash-string
URL::signedRoute('unsubscribe', ['user' => 1]);
С первого взгляда не совсем очевиден смысл этй подписи. А весь смысл в том, что пользователь не сможет самостоятельно сгенерировать подпись, т.к. он не знает ключевое слово, которое используется для ее создания, а без нее ссылка не имеет смысла.
Если посмотреть на код под капотом мы увидим что-то похожее на тот код, который я привел ниже
Т.е. для удобства все параметры сортируются по ключу и далее генерируется чистый url ($url)
, для которого генерируется хэш-код с использованием ключа $key
и если постороннему человеку этот ключ не извевстен, то повторить эту процедуру он не сможет.
После того, как пользователь перешел по отправленной ему ссылке происходит обратная процедура
Т.е. сервер генерирует хэш на основе тех данных, которые пришли от сервера и сверяет с тем хэшом, который пришел вместе с запросом, все предельно просто.
Таким же образом работает проверка подписанного временного URL
// http://site.com/unsubscribe?user=1&expires=1593725346&signature=hash-string
URL::temporarySignedRoute(
'unsubscribe', now()->addMinutes(30), ['user' => 1]
);
Т.е. в запрос также добавляется метка времени, когда истек срок действия ссылка и генерируется хэш с учетом этого времени.
После проверки валидности подписи из запроса достается timestamp из параметра expires
и производится проверка
public function signatureHasNotExpired(Request $request)
{
$expires = $request->query('expires');
return ! ($expires && Carbon::now()->getTimestamp() > $expires);
}
В некоторых ситуациях есть необходимость подписывать не URL, а непосредственно тело ответа POST запрос и валидировать их при передачи подписанных данных в другой API запрос. Представим ситуацию, где пользователь запрашивает стоимость некого абстрактного товара, стоимость которого динамическая и зависит от разных внешних факторов (например курс доллара) и далее пользователь хочет по этой стоимости оплатить товар. Мы можем подписать результат, который вернул нам сервер и указать срок жизни данного расчета
Таким образом зная все 4 параметра на другой стороне можно произвести проверку подписи и удостовериться, что в нашем запрос ни один из этих 4-х параметром, на основе которых был сегенерирован хэш не был изменен и что стоимость товара та, что вернул сервер.
При желании генерацию подписи можно оформить в виде сервиса, который будет заниматься генерацией и проверкой данных. В примере ниже я объединил подпись с меткой времени срока жизни подписи.
Ну вот собственно и все.
Буду рад, если кому то поможет в изучении Laravel да и в целом в развитии.
P.S. пишите предложения о том, о чем бы вы хотели почитать.