Коли я залишила однакову S3-конфігурацію для local і production, для мене було важливо зберегти не тільки однаковий технічний контракт, а й однаковий зміст роботи з файлами.
Якщо файл у production має бути приватним, локально він теж не повинен ставати публічним лише тому, що так простіше перевірити його в браузері.
Інакше локальне середовище перестає перевіряти той сценарій, який насправді буде працювати після розгортання.
Публічний бакет здається зручним рішенням
Upload file | vPublic bucket | vCopy URL | vOpen in browser Дуже просто. Але access flowвже не такий,Під час локальної розробки виникає природне бажання просто зробити бакет публічним.
Тоді файл легко перевірити: скопіювати адресу і відкрити її в браузері.
Але якщо production-файл повинен відкриватися лише після перевірки прав користувача, такий локальний сценарій обходить найважливішу частину поведінки.
Приватний файл не повинен відкриватися лише тому, що відома його адреса
Known object path private/video/example.mp4 | vDirect anonymous request | XNo permanent public access Access must come throughan allowed application flow.Для приватного файла сам шлях до об’єкта не повинен бути достатнім дозволом.
Користувач може знати назву файла або навіть повний ключ об’єкта, але це не означає, що сховище повинно віддати його без додаткового механізму доступу.
Саме таку поведінку я хочу бачити і локально.
Приватність не потребує окремого способу збереження файла
Storage::disk('s3')->put( $path, $contents,);Сам файл я продовжую зберігати через стандартний S3 disk Laravel.
Приватність не означає, що застосунку потрібен інший драйвер або окремий PrivateMinioService.
Вона означає, що сховище не повинно давати постійний анонімний доступ до цього об’єкта.
Я не позначаю приватний файл як public лише для локальної перевірки
Storage::disk('s3')->put( $path, $contents, 'public',);Storage::disk('s3')->put( $path, $contents,);Якщо правила системи кажуть, що файл приватний, я не передаю public лише тому, що хочу швидко відкрити його за прямою адресою.
Інакше тест проходить саме тому, що я прибрала обмеження, яке мала перевірити.
Не роблю весь бакет публічним заради одного простого сценарію
Private bucket object A -> privateobject B -> privateobject C -> private Temporary local shortcut: "Make bucket public" | v A, B, C becomeanonymous-accessibleОсобливо небезпечна звичка - відкривати весь локальний бакет лише для того, щоб не думати про доступ до окремих файлів.
Тоді локальна система більше не покаже, що якийсь екран випадково покладається на постійний публічний URL.
Я краще одразу залишу бакет приватним і змушу застосунок працювати через правильний механізм доступу.
Приватний бакет ще не замінює перевірку прав у застосунку
User requests file | vApplication | | authorize? vAllowed | vControlled storage access | vPrivate objectЦі два рівні важливо не змішувати.
Приватний бакет захищає об’єкт від постійного анонімного доступу.
А застосунок вирішує, чи конкретному користувачу взагалі дозволено отримати цей файл.
Тому навіть приватне сховище не є заміною перевірки прав.
Постійний прямий URL не повинен ставати частиною моделі приватного файла
Database file_url =https://storage.example/private/video.mp4 Application later assumes: "If I have the URL,I can always open the file."Коли локально файл завжди доступний за постійною URL, цей факт легко починає проникати в інші частини системи.
URL зберігається в базі, передається у відповіді API або напряму вставляється в інтерфейс.
Після переходу до приватного production-сховища виявляється, що весь цей код спирався на властивість, якої насправді не мало бути.
Для приватного файла мені важливіший ключ об’єкта, ніж постійна публічна адреса
private/support-video/4f88b7a1-example.mp4Для приватного сховища мені достатньо знати, де саме лежить об’єкт.
А спосіб отримання доступу може визначатися вже в момент запиту.
Це не прив’язує запис у базі до вічно доступної публічної адреси.
Приватний файл потребує контрольованого способу читання
$url = Storage::disk('s3') ->temporaryUrl( $path, now()->addMinutes(10), );Один із можливих механізмів - тимчасове підписане посилання.
Його важлива властивість у тому, що доступ не стає постійним.
Спочатку застосунок може перевірити право користувача, а потім видати короткоживучий доступ до конкретного об’єкта.
Цей механізм я розберу окремо далі в серії.
Локальне середовище повинно вміти правильно відмовляти в доступі
Хороше локальне середовище перевіряє не тільки успішні сценарії.
Для приватного файла мені важливо побачити і правильну відмову: прямий анонімний запит не отримує об’єкт.
Саме така невдала спроба часто доводить, що модель приватності працює.
Публічний бакет може приховати помилку до самого production
Local file is public | vUI opens direct URL | vEverything works Production file is private | vЯ намагаюся не послаблювати security-модель лише для local
Локальне середовище не повинно копіювати всі production-налаштування буквально.
Але правила, які впливають на поведінку коду, я намагаюся зберігати.
Приватність файла якраз належить до таких правил.
Якщо локально її вимкнути, ми тестуємо не спрощену інфраструктуру, а інший сценарій доступу.
Що я перевіряю для приватного файла
1. Laravel can write the object -> yes 2. Object exists in the bucket -> yes 3. Direct anonymous access -> denied 4. Authorized application flow -> allowed Що я перевіряю перед тим, як зробити S3-файл публічним
Мій короткий список:
- Чи файл за правилами продукту справді має бути публічним?
- Чи я відкриваю його лише заради зручності локальної перевірки?
- Чи production-файл буде приватним?
- Чи прямий URL випадково не став частиною API-контракту?
- Чи зберігаю я ключ об’єкта замість припущення про постійний публічний URL?
- Чи застосунок перевіряє право користувача перед доступом?
- Чи прямий анонімний запит правильно отримує відмову?
- Чи локальна модель доступу відповідає production-моделі?
Локальна зручність не повинна змінювати правила доступу
Зробити локальний бакет публічним дуже легко.
Але якщо production-сховище приватне, така зручність може приховати неправильні припущення в коді.
Інтерфейс починає покладатися на прямі URL, API повертає постійні адреси, а перевірка прав фактично обходиться.
Я краще побачу ці проблеми локально.
Тому приватні файли залишаються приватними ще під час розробки, а доступ до них проходить через той самий контрольований сценарій, який потрібен після розгортання.
Локальне середовище може бути простішим за production, але воно не повинно змінювати security-семантику файла.
Якщо файл має бути приватним після розгортання, я не роблю його публічним під час розробки лише для того, щоб було зручніше.



