安全地提供图像

时间:2011-01-30 21:20:07

标签: php security image overhead

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

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

我无法弄清楚风险是什么,或者为什么在通过剧本提供服务时可以避免这些风险 这样一个脚本的开销听起来很荒谬。

我确实理解在存储到服务器之前必须检查图像,为此我使用imagemagick做一个小的转换并保存到jpeg - 这应该摆脱任何渣滓,据我所知。

那么,关于SO的伟大思想的问题:

  1. 使用易于遵循的路径在本地存储图像是否存在安全问题?
  2. 我的方法是使用IM安全审查图像吗?
  3. 是否有理由使用PHP来提供图像?
  4. 使用PHP的开销是否真的很大?
  5. 使用CDN会在安全性方面有所作为(我不想)?
  6. 我错过了什么吗?
  7. 全部谢谢!

4 个答案:

答案 0 :(得分: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是否能解决安全问题没有一个好的答案。至少,请务必使用当前版本,因为快速搜索会发现许多已知漏洞。请记住,您需要担心的文件可能会破坏图像效果,即使它们没有利用其中一个已知漏洞,因此请确保错误捕获真正的错误。

答案 1 :(得分:7)

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

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

答案 2 :(得分:5)

  

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

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

如果他们可公开访问(即只有登录用户才能访问),那么您的同事无法描述(或在执行后使用X-Sendfile登录检查,如果安装了必要的软件,可以节省部分开销。但如果不是这种情况,那么所需的开销是 crazy

答案 3 :(得分: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();