12

一位受人尊敬的同事坚持认为在我的服务器上存储图像是不安全的,特别是如果文件结构很容易推测(因为我们有用户创建的图像库,命名方案很容易遵循)。

相反,他建议将图像存储在根目录之上,并使用 fread 或 fputthrough 为它们提供服务。

我无法弄清楚风险是什么,或者为什么通过脚本提供这些风险会被消除。
这样一个脚本的开销听起来很荒谬。

我确实知道在存储在服务器上之前必须检查图像,为此我使用 imagemagick 进行小转换并保存到 jpeg - 据我猜测,这应该可以消除任何渣滓。

所以,关于SO的伟大思想的问题:

  1. 通过易于遵循的路径在本地存储图像是否存在任何安全问题?
  2. 我使用 IM 审查图像的方法是否安全?
  3. 是否有理由使用 PHP 来提供图像?
  4. 使用 PHP 的开销真的很大吗?
  5. 就安全性而言,使用 CDN 会有所不同吗(我不想这样做)?
  6. 我错过了什么吗?

谢谢大家!

4

4 回答 4

9

我怀疑您的朋友所指的不仅仅是提供图像,而是专门提供用户提供的图像。在提供用户提供的内容时存在许多安全问题。对于图像,有多种方法可以使用图像上传来让 Web 服务器执行代码。一些比较知名的包括:

  • 一个“GIFAR”文件。本质上,这是一个 GIF 文件和一个 jar 文件连接在一起。因为 GIF 的索引信息在开头,而 jar 文件的索引信息在结尾,所以这两种文件类型可以组合起来,结果既是有效的 GIF,又是有效的 JAR。
  • 多个文件扩展名。Web 服务器支持多个文件扩展名以允许国际化之类的事情。例如,名为 page.html.fr 的文件可能映射到页面的法语版本。扩展名为 image.php.jpg 或什至 image.php.123 的文件可以作为 PHP 脚本执行,具体取决于服务器的配置。
  • 缓冲区溢出。图像文件格式通常在开头包含一个标题,用于描述文件的大小和格式。低估大小可以让恶意用户制造缓冲区溢出攻击。

所有这些示例都导致了将代码作为 Web 服务器执行的能力。尽管该站点的功能需要能够上传文件,但将它们存储在 URL 无法直接访问的目录中会使利用它们变得更加困难。同样,使用脚本而不是 Web 服务器的 MIME 处理程序来为它们提供服务可确保将图像视为数据流而不是潜在的可执行文件。

如此多的安全性是否荒谬取决于用户的数量、收集的有关他们的数据的性质以及网站的琐碎性。至少,攻击者希望获取用户的密码,因为他们倾向于同步它们。您网站上密码为 ABC123 的用户可能会对电子邮件、社交网站以及可能的银行和金融网站使用相同的密码。除了密码之外,如果您收集的有关用户的数据可以识别个人身份或具有其他市场价值,或者您只是拥有大量用户,那么您必须假设该网站将成为目标。这意味着,要么更加小心地提供用户提供的图像文件,要么非常好地验证它们,或者两者兼而有之。

对于使用 imagemagick 是否会解决安全问题,我没有很好的答案。至少,一定要使用当前版本,因为快速搜索会发现许多已知漏洞。请记住,您需要担心的文件可能会破坏 imagemagick,即使它们没有利用已知漏洞之一,因此请确保具有非常好的错误捕获。

于 2011-01-31T03:02:51.287 回答
7

如果图像是非私密的,也就是说,你可以很容易地看到它们,你只是不想让别人随意阅读它们,给每个图像一个唯一的名字就足够了。

如果有超过几千个(看起来很可能),那么创建存储它们的目录层次结构也可以帮助加快访问速度,同时增加搜索空间,以至于随机搜索无法找到它们。例如:可以将名为 347168a5d9b4ac5eb386e396f68b2231.jpg 的文件放入目录:/images/34/71/68/347168a5d9b4ac5eb386e396f68b2231.jpg 多级目录通过避免扫描数千个目录条目来加快访问速度,并且随机名称 (存储在附加到用户图库中的图像列表的数据库中)阻止随机文件被发现。

于 2011-01-30T21:53:45.773 回答
5

这样一个脚本的开销听起来很荒谬。

同样在这里。如果图像可以公开访问,我认为绝对没有理由这样做。

如果它们不可公开访问(即必须仅对登录用户可访问),则无法绕过您同事描述的方式(或使用X-Sendfile执行登录检查后的方式,如果必要的软件是,则可以节省部分开销安装)。但如果不是这种情况,那么所需的开销是疯狂的。

于 2011-01-30T21:25:07.510 回答
0

如果您确定您确实需要图像是安全的,那么这就是我在漫画项目中使用的(基本上我将大量漫画放在一个目录中,然后当某个时间命中时,我会允许访问它们一次一个)

//check if image should be accessed by the current request...

$fh=fopen($my_image,"rb");

if($fh==NULL){
        exit();
}
rewind($fh);
header("Content-Type: image/png");
header("Content-Length: " . filesize($fn));
fpassthru($fh);

fclose($fh);
exit();
于 2011-01-31T03:26:59.390 回答