Laravel从php输出缓冲区下载文件VS.私有存储文件夹|安全性



用户可以下载CSV格式的查询结果。文件很小(几KB(,但内容很重要。

第一种方法是使用 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

原因:

  • 您没有保留文件,因此持久保存到磁盘的用途有限
  • 数据大小很小,因此不太可能下载失败,如果发生失败,重新创建输出的处理时间最短(我认为这是幕后的快速SQL查询?
  • 将文件保存在存储中会为文件复制创造机会,您将来可能设置的增量备份或 rsync 可以在敏感文件被删除之前复制它们......
  • 从文件系统中删除文件并不一定会使数据无法恢复

如果您正在处理数十/数百 MB 的文件,我会有不同的想法......

让我们考虑所有选项,

选项 1是一个很好的解决方案,因为您不存储文件。它将比其他更安全。但是在高流量下超时可能是一个问题。

选项 2也是删除的好解决方案。但是,您需要创建具有唯一名称的文件,以便可以使用并行下载。

选项3 类似于选项2,但如果您使用的是 laravel,请不要使用它。(并考虑2个人同时下载(

在此说明之后,您需要处理选项 1,以便在使用一台服务器时使其更安全。但是,如果您使用的是微服务,则需要处理选项 2。

我可以再建议一件事来确保它的安全。创建唯一的哈希 URL。例如,使用时间戳并使用 laravel 对其进行哈希处理,并在 URL 之前检查它们。因此,用户无法从下载历史记录中再次下载。

https://example.com/download?hash={crypt(timestamp+1min(}

如果在 1 分钟内未下载,URL 将过期。

我认为回复取决于当前的架构和要下载的文件的大小

(1(ST方法适用于以下情况:

  • 如果文件很小(小于10 Mb(,谢谢@tanerkay

  • 您有简单的架构(例如 1 台服务器(

原因:

  • 无下载失败 -- 无需重试

  • 保持简单

  • 没有文件 = 没有备份,没有 rsync 也没有额外的地方来窃取它

. . .

(2(第二种方法适用于以下情况:

  • 如果您的文件很大 (10+ Mb(

  • 如果您已经拥有具有多个余额加载器的微服务架构 - 请保持相似性

  • 如果您有数百万用户尝试下载 - 如果没有平衡加载器和并行下载,您将无法为他们提供服务

原因:

  • 第二种方法肯定更具可扩展性,因此在高负载下更稳定,因此更安全。微服务更耗时,并且对于繁重负载更具可扩展性。

  • 使用单独的文件
  • 存储允许您将来加载单独的文件服务器和平衡,以及队列管理器和单独的专用访问控制。

  • 如果内容很重要,通常意味着获取它对用户非常重要。 但是带有标头的直接输出可能会挂起或出现超时错误等。 我认为,将文件保留到下载之前是交付文件的更确定的方法。

尽管如此,我还是考虑过期时间而不是下载事实 - 下载过程可能会失败,或者文件丢失(确保 1+ 小时可用性(,反之亦然,用户只会在 1 年后尝试下载它或永远不会 - 为什么要保留此文件超过 N 天?

我认为第一个选项

The first approach is to use php output buffer php://:

比其他不将文件存储在任何地方的地方更安全。

最新更新