Після того як я залишила приватні 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 лише для того, щоб браузер міг його відкрити.
Приватність визначає, хто може отримати доступ. Тимчасове посилання визначає, як цей уже дозволений доступ безпечно доставити до браузера на обмежений час.
Я не відкриваю приватний файл назавжди. Я видаю доступ до нього лише тоді, коли він потрібен, тому користувачу, якому він дозволений, і лише на обмежений час.



