Laravel скачать файл из выходного буфера php VS. личная папка хранения | безопасность
Пользователь может загрузить результаты запроса в формате CSV. Файл небольшой (несколько КБ), но его содержимое важно.
Первый подход заключается в использовании выходного буфера php php://
:
$callback = function() use ($result, $columns) {
$file = fopen('php://output', 'w');
fputcsv($file, $columns);
foreach($result as $res) {
fputcsv($file, array($res->from_user, $res->to_user, $res->message, $res->date_added));
}
fclose($file);
};
return response()->stream($callback, 200, $headers);
Второй подход - создать новую папку в системе хранения laravels, установить ее в приватную и загрузить файл оттуда. Вы даже можете удалить файл после загрузки:
'csv' => [
'driver' => 'local',
'root' => storage_path('csv'),
'visibility' => 'private',
],
Вот код создания/загрузки:
$file = fopen('../storage/csv/file.csv', 'w');
fputcsv($file, $columns);
foreach($result as $res) {
fputcsv($file, array($res->from_user, $res->to_user, $res->message, $res->date_added));
}
fclose($file);
return response()->make(Storage::disk('csv')->get('file.csv'), 200, $headers);
Этот возврат сразу удалит файл после загрузки:
return response()->download(Storage::disk('csv')->path('file.csv'))
->deleteFileAfterSend(true);
Что будет безопаснее? Какой подход лучше? Я в настоящее время склоняюсь ко второму подходу с хранилищем.
Ответы
Ответ 1
Вариант 1
Причины:
- вы не сохраняете файл, поэтому его сохранение на диске ограничено
- размер данных невелик, поэтому сбои при загрузке маловероятны, и если они произойдут, время обработки для воссоздания вывода будет минимальным (я полагаю, это быстрый SQL-запрос за кулисами?)
- хранение файла в хранилище создает возможности для репликации файла, инкрементное резервное копирование или rsync, которые вы можете настроить в будущем, могут реплицировать конфиденциальные файлы до того, как они будут удалены...
- удаление файла из файловой системы не обязательно делает данные невосстановимыми
Если бы вы имели дело с файлами размером в десятки/сотни МБ, я бы подумал иначе...
Ответ 2
Давай подумаем обо всех вариантах,
Вариант 1 является хорошим решением, потому что вы не храните файл. Это будет более безопасно, чем другие. Но время ожидания может быть проблемой при большом трафике.
Вариант 2 также хорошее решение с удалением. Но вам нужно создавать файлы с уникальными именами, чтобы вы могли использовать параллельные загрузки.
Вариант 3 похож на вариант 2, но если вы используете laravel, не используйте его. (И подумайте, что 2 человека загружают одновременно)
После этого объяснения вам нужно поработать над вариантом 1, чтобы сделать его более безопасным, если вы используете один сервер. Но если вы используете микросервисы, вам нужно работать над вариантом 2.
Я могу предложить еще одну вещь, чтобы сделать его безопасным. Создайте уникальный хэшированный URL. Например, используйте метку времени и хеш с laravel и проверьте их перед URL. Таким образом, люди не могут загрузить снова из истории загрузок.
https://example.com/download?hash={Крипты (временная метка + 1min)}
Если он не будет загружен в течение 1 минуты, срок действия URL истечет.
Ответ 3
Я думаю, что ответ зависит от текущей архитектуры и размера файла для загрузки
(1) Первый подход применим, когда:
Если файлы небольшие (менее 10 Мб), спасибо @tanerkay
у вас простая архитектура (например, 1 сервер)
Причины:
нет ошибок загрузки - не нужно повторять
будь проще
нет файлов = нет резервных копий и нет rsync и нет дополнительных мест, чтобы украсть его
,
,
.
(2) Второй подход применим, когда:
Если ваши файлы большие (10+ Мб)
Если у вас уже есть архитектура микросервисов с несколькими загрузчиками баланса - сохраняйте сходство
Если у вас есть миллионы пользователей, пытающихся загрузить - вы просто не сможете обслуживать их без загрузчика баланса и параллельной загрузки
Причины:
Второй подход, безусловно, более масштабируемый и более стабильный при высокой нагрузке, поэтому более безопасный. Микросервисы более трудоемки и более масштабируемы для большой нагрузки.
Использование отдельного хранилища файлов позволяет в будущем иметь отдельный загруженный файловый сервер и баланс, а также менеджер очередей и отдельный выделенный контроль доступа.
Если содержание важно, это обычно означает, что получить его очень важно для пользователя.
Но прямой вывод с заголовками может зависнуть или получить ошибку тайм-аута и так далее.
Я думаю, что хранить файл до тех пор, пока он не будет загружен, - гораздо более надежный подход к его доставке.
Тем не менее, я рассматриваю время истечения вместо или в дополнение к факту загрузки - процесс загрузки может завершиться неудачно, или файл потерян (обеспечьте доступность 1+ часа) или наоборот, пользователь попытается загрузить его только через 1 год или никогда - почему вы должны хранить этот файл более N дней?
Ответ 4
Я думаю, что первый вариант
The first approach is to use php output buffer php://:
более безопасен, чем другие, где вы нигде не храните файл.