Два типи файлів - два способи зберігання:
- публічні (аватари, зображення статей) - диск
publicчи публічний бакет; доступні за прямим URL усім; - приватні (рахунки, документи, вкладення в листуванні) - диск
local(каталогstorage/app/private) чи приватний бакет; недоступні за прямим URL.
Типова помилка - класти все на public. php artisan storage:link робить storage/app/public доступним з вебу. Документ, збережений туди, відкривається будь-ким, хто вгадає чи отримає шлях - а шляхи потрапляють у листи, логи, історію браузера.
Віддача приватних файлів - через контролер з перевіркою прав:
Route::get('/documents/{document}', function (Document $document) {
Gate::authorize('view', $document);
return Storage::disk('private')->download($document->path, $document->original_name);
})->middleware('auth');
Для хмарних сховищ - тимчасове підписане посилання, файл віддає саме сховище:
return redirect(Storage::disk('s3')->temporaryUrl($document->path, now()->plus(minutes: 5)));
Правила збереження:
- власні назви файлів -
$file->store('documents')генерує випадкову назву. Оригінальну назву зберегти в базі для показу, але не використовувати як шлях (обхід шляху, перезапис чужих файлів, спецсимволи); - перевірка вмісту, а не розширення: правило
mimes:pdf,jpgперевіряє MIME за вмістом;extensions:- лише розширення. Для зображень корисно ще й перекодувати (знімає вбудовані скрипти й метадані з геолокацією); - обмеження розміру - правило
max:і ліміти PHP/вебсервера; - жодного виконання в каталогах завантажень: вебсервер не повинен виконувати
.phpзstorageчи публічних каталогів; Content-Disposition: attachmentдля файлів, які не повинні відкриватися в браузері (HTML чи SVG від користувача, відкриті як сторінка вашого домену, - XSS).
Видимість у хмарі: у Laravel/Flysystem файли за замовчуванням приватні; 'visibility' => 'public' - свідоме рішення. Бакет без публічного доступу на рівні налаштувань провайдера - додатковий захист від помилки в коді.
Видалення: при видаленні запису видаляти й файл (і навпаки - прибирати «осиротілі» файли), інакше дані, які користувач вважає видаленими, лишаються доступними.