Підписаний URL містить параметр signature - HMAC від адреси, обчислений з APP_KEY. Будь-яка зміна URL (інший id, інший термін) робить підпис недійсним.
use Illuminate\Support\Facades\URL;
// безстроковий
$url = URL::signedRoute('unsubscribe', ['user' => $user->id]);
// з терміном дії
$url = URL::temporarySignedRoute('invoices.download', now()->plus(hours: 24), ['invoice' => $invoice->id]);
// https://example.com/invoices/42/download?expires=1760000000&signature=9a1f...
Перевірка на маршруті:
Route::get('/invoices/{invoice}/download', DownloadInvoice::class)
->name('invoices.download')
->middleware('signed');
Недійсний чи прострочений підпис - відповідь 403. Вручну - $request->hasValidSignature().
Де доречні:
- відписка від розсилки одним кліком з листа - без входу в обліковий запис;
- підтвердження email (так працює стандартна верифікація Laravel);
- тимчасові посилання на файли - звіти, рахунки, експорт, які надсилаються поштою;
- «чарівні посилання» для входу без пароля;
- запрошення в команду чи проєкт.
Що важливо розуміти:
- підпис захищає від зміни URL, а не від передачі. Хто має посилання, той має доступ. Тому для чутливих дій - короткий термін, а для «чарівних посилань» входу - ще й одноразовість (позначка в базі, що посилання використано);
- підписується вся адреса з параметрами. Додатковий параметр, доданий після підписання (
?utm_source=...від поштового сервісу), зламає підпис. Для таких випадків -hasValidSignatureWhileIgnoring(['utm_source'])абоsigned:relativeдля підпису лише шляху; - підпис залежить від
APP_KEYі домену - зміна ключа безAPP_PREVIOUS_KEYSзламає всі видані посилання; за проксі Laravel має знати правильну схему й хост (trustProxies), інакше перевіркаhttps-посилання наhttp-запиті не пройде; - підписане посилання потрапляє в історію браузера, логи й поле
Referer- не кладіть туди нічого, що має лишатися секретом довше за термін дії.
Для файлів на S3/R2 аналог - Storage::temporaryUrl(): підписує вже сховище, і файл віддається без участі застосунку.