0% прочитано

Тимчасове посилання на приватний файл у Laravel: як працює temporaryUrl()

Приватному файлу не потрібна постійна публічна адреса. Laravel може створити короткоживуче підписане посилання лише тоді, коли користувачу справді дозволено отримати файл.

10 жовтня 2026 р. 9 хв читанняLaradock

Після того як я залишила приватні S3-файли приватними ще під час локальної розробки, виникло наступне практичне питання: як тоді показати такий файл користувачу?

Робити весь об’єкт публічним лише заради читання не потрібно.

Laravel може створити короткоживуче підписане посилання на конкретний приватний об’єкт.

Сам файл при цьому не стає публічним назавжди.

Тимчасове посилання не робить бакет публічним. Воно дає обмежений у часі доступ до конкретного об’єкта.

Приватному файлу не потрібна постійна публічна адреса

Публічність не потрібна лише заради перегляду
Private file    |    vmake public    |    vpermanent URL    |    vanyone who knows itcan request the object  Зручніше,але модель доступувже неправильна.

Якщо файл є приватним за правилами системи, я не хочу перетворювати його на публічний лише тому, що користувачу треба відкрити його один раз.

Правильніше залишити об’єкт приватним і видати контрольований доступ лише на потрібний час.

У Laravel для цього є temporaryUrl()

Короткоживучий доступ до приватного об’єкта
$url = Storage::disk('s3')    ->temporaryUrl(        $path,        now()->addMinutes(10),    );

temporaryUrl() отримує шлях до об’єкта і момент завершення дії посилання.

Laravel через S3-драйвер створює підписаний запит, який сховище може перевірити без зміни приватності самого об’єкта.

Після завершення строку те саме посилання більше не повинно давати доступ.

Для цього не потрібен окремий MinIO-сервіс

Для створення тимчасового посилання я використовую той самий стандартний S3 disk Laravel, через який зберігаю приватний файл.

Застосунку не потрібен окремий MinioTemporaryUrlService лише через те, що локальним S3-сумісним сховищем є MinIO.

Важливим залишається контракт S3, а не назва постачальника сховища.

Перевірка права має відбутися до створення посилання

Спочатку право, потім посилання
$this->authorize(    'view',    $document,); $url = Storage::disk('s3')    ->temporaryUrl(        $document->storage_path,        now()->addMinutes(10),    ); return [    'url' => $url,];

temporaryUrl() не знає, хто є користувачем і чи має він право бачити файл.

Це відповідальність застосунку.

Тому порядок для мене принциповий: спочатку знайти потрібний запис, перевірити право доступу і лише після цього створити тимчасове посилання.

Саме посилання на короткий час стає ключем доступу

Після видачі посилання браузер може звернутися до сховища напряму
User  |  | authenticated request  vApplication  |  | authorization  vtemporary signed URL  |  vBrowser  |  | direct request  vS3-compatible storage

Після створення такого посилання наступний запит може йти безпосередньо до S3-сумісного сховища.

Сховище перевіряє підпис і строк дії, а не сесію Laravel.

Тому саме посилання потрібно сприймати як короткоживучі облікові дані.

Якщо користувач передасть його іншій людині, вона теж може скористатися ним, доки посилання дійсне.

Строк дії має відповідати реальному сценарію

Строк не повинен бути випадковим
now()->addMinutes(5) now()->addMinutes(10) now()->addMinutes(30)

Я не ставлю великий строк лише для того, щоб користувач "точно встиг".

Посилання має жити достатньо довго для нормального сценарію читання або завантаження, але не значно довше.

Для різних типів файлів цей час може відрізнятися.

У базі я зберігаю шлях до об’єкта, а не тимчасове посилання

Тимчасове посилання створюється тоді, коли воно потрібне
Database storage_path =private/documents/example.pdf  At request time storage_path   |   vauthorization   |   vtemporary URL   |

Тимчасове посилання має строк дії, тому зберігати його як довгострокове поле в базі немає сенсу.

Я зберігаю стабільний ключ або шлях об’єкта.

Коли користувачу потрібен доступ, застосунок повторно перевіряє право і створює нове посилання.

Тимчасове посилання не повинно непомітно стати постійною частиною API

Якщо API одного разу повертає підписане посилання, клієнт може почати зберігати його надовго і очікувати, що воно працюватиме завжди.

Це неправильне припущення.

Тимчасова адреса є результатом конкретного запиту на доступ, а не сталою адресою файла.

Короткий строк дії не замінює перевірку прав

Посилання на п’ять хвилин усе одно не можна видавати користувачу, який не має права на файл.

Обмеження часу зменшує вікно доступу, але не вирішує питання авторизації.

Тому я розділяю дві речі: хто може отримати посилання і скільки часу це посилання буде дійсним.

Якщо об’єкт видалений, старе посилання вже не повинно допомогти

Підпис підтверджує право виконати конкретний запит за визначеними параметрами.

Він не створює копію самого файла.

Якщо об’єкт уже видалений зі сховища, стара підписана адреса не поверне його назад.

Локально перевіряю не тільки підпис, а й адресу, яку отримає браузер

Серверна адреса і браузерна адреса можуть відрізнятися
Laravel container http://minio:9000        |        vMinIO  Browser cannot normally resolvethe Docker service name"minio"

Це особливо важливо з локальним MinIO.

Laravel усередині Docker може чудово бачити http://minio:9000, але браузер на комп’ютері не зобов’язаний знати внутрішнє ім’я сервісу minio.

Тому успішне створення підписаного посилання ще не є повною перевіркою.

Я відкриваю отриману адресу саме в тому середовищі, де нею користуватиметься браузер.

Що я перевіряю для temporaryUrl()

Перевіряю весь сценарій доступу
1. File is private   -> direct permanent access denied  2. Authorized user asks for file   -> temporary URL returned  3. URL works before expiration   -> file available  4. URL expires   -> old URL no longer works  

Найгірший короткий шлях - зробити файл public замість виправлення temporaryUrl()

Не виправляю проблему послабленням приватності
Storage::disk('s3')->put(    $path,    $contents,    'public',);

Якщо локальне підписане посилання не відкривається через неправильну адресу, дуже легко вирішити проблему неправильним способом: просто зробити об’єкт публічним.

Я цього не роблю.

Проблему формування або доступності тимчасового посилання потрібно виправити окремо, не змінюючи модель приватності файла.

Мій короткий список для тимчасових посилань

Я перевіряю:

  • Чи об’єкт залишається приватним?
  • Чи право користувача перевіряється до temporaryUrl()?
  • Чи строк дії відповідає реальному сценарію?
  • Чи в базі зберігається шлях до об’єкта, а не тимчасова адреса?
  • Чи клієнт не сприймає тимчасове посилання як постійну адресу?
  • Чи браузер може відкрити згенеровану адресу?
  • Чи прямий анонімний доступ без підпису залишається забороненим?
  • Чи прострочене посилання більше не працює?

Приватний файл може бути доступним без перетворення на публічний

temporaryUrl() добре вписується в модель приватного S3-сховища.

Файл залишається приватним. Застосунок вирішує, хто має право його отримати. Лише після цього створюється короткоживуче підписане посилання на конкретний об’єкт.

У базі при цьому залишається стабільний шлях до файла, а не адреса, яка скоро стане недійсною.

Так я не змушую приватний файл мати постійний публічний URL лише для того, щоб браузер міг його відкрити.

Приватність визначає, хто може отримати доступ. Тимчасове посилання визначає, як цей уже дозволений доступ безпечно доставити до браузера на обмежений час.

Я не відкриваю приватний файл назавжди. Я видаю доступ до нього лише тоді, коли він потрібен, тому користувачу, якому він дозволений, і лише на обмежений час.