【问题标题】:Secure image downloading安全图像下载
【发布时间】:2012-09-18 01:47:11
【问题描述】:

我一直在研究如何安全地上传图片,并且很明显,实际提供图片是最有风险的地方,因为恶意 PHP 文件可能会危及服务器。

许多人建议使用图像处理脚本,例如 getimage.php?i=575748,它会在数据库 575748 中查找并实际返回 myphoto.jpg(用户不知道原始图像的位置)更安全。

能否指出我应该在脚本中实现什么样的事情的正确方向?我知道设置内容类型标头是必须的,但这足以阻止代码执行吗?

谢谢!

【问题讨论】:

  • 为什么您服务器上的图像甚至可以执行? IE 为什么 PHP 用户(没有人或 apache 最有可能)对带有图像扩展名的文件具有可执行权限?
  • 目前它们不是,但是我想消除用户能够将文件扩展名更改为 say.php 然后执行它的风险。我认为通过脚本传递图像,他们无法知道文件存储在哪里,因此直接访问它会变得更加困难?
  • 如果您担心不良图像进入系统,请在图像存储之前对图像运行getimagesize。如果文件实际上不是图像,这将返回 false。
  • @anjunatl 一个问题是文件可以同时是图像和另一种格式。例如,有一个臭名昭著的 gif 同时是一个罐子。

标签: php security file


【解决方案1】:

而且很明显,实际提供图像的位置 大多数风险是针对可能危及 服务器

这只有在满足两个条件时才会发生:

  • 您允许上传非图片文件
  • 通过直接链接完成下载

阻止其中一个,危险就会消失。当然关闭两者更好。

数据库解决方案旨在破坏条件#2,但您也可以在上传图片时使用getImageSize()函数检查它确实是一张图片。或者尝试使用imageCreateFromJPEG() 或其他适当的功能加载图像。

如果加载的对象不是图像,则只显示“对不起,图像已损坏”。这既可以保护您的用户免于上传损坏的图像,也可以保护您免于有人上传流氓 PHP 文件(或者可能是受版权保护的视频或破解的可执行文件或......)。

更新:您还可以混淆现有的上传和下载工具:

假设您当前上传了用户图片,为其分配一个唯一 ID,将其保存为 ./images/UNIQUEID.jpg,并作为 URL 提供给用户 http://www.yoursite.com/images/UNIQUEID.jpg

  • 您应用.htaccess,以便对./images/whatever 的访问重定向到./images/index.php?ID=whatever
  • 您创建一个 index.php 脚本,该脚本接受“任何内容”、提取图像 ID 并输出图像。

所有旧 URL 将继续工作,并且 似乎 可以直接加载图像,但它们实际上正在调用一个 PHP 脚本来检查图像并将其作为字节流发送,无需翻译。

您需要将ETag 设置为图像文件的ID,Last-Modified 设置为file_mtime(),并将Content-Length 设置为文件大小。另外,您可能要检查传入的标头If-Modified-SinceIf-None-Match:如果文件的日期对应于IMS 或IFN 值对应于ID,则可以发送HTTP/304 Not Modified 答案而不是图像,保存相当大的带宽。

【讨论】:

    【解决方案2】:

    为了一件事,将上传文件夹放在根文件夹之外(即 /www/ 或 /htdocs/ 或 /httpdocs/ 文件夹之外)。设置文件夹的权限,以便只有本地用户可以写入。

    至于数据库的想法,你当然可以继续做,结合上面提到的想法,应该是一个相当安全的图像上传服务。

    所涉及的唯一风险是当您没有对要上传到的文件夹设置正确的权限时,这确实可能会危及服务器。

    【讨论】:

      【解决方案3】:

      啊,我明白了。如果您不想让它们在文件系统中浮动,您也可以将图像存储在数据库中。无论您喜欢哪种方式。使用脚本而不是 Apache 来提供图像的问题是图像必须加载到内存中。因此,请为性能影响做好准备。

      【讨论】:

      • 在数据库中存储图像通常是错误的选择。图像不必“加载到内存中”来提供它们。
      • @MarcusAdams 我的印象是,除非您将 sendfile mod 用于 apache,否则您会这样做。 jasny.net/articles/how-i-php-x-sendfile
      【解决方案4】:

      解决您最初的安全问题,在图像存储之前在图像上运行getimagesize() - 如果您从该功能中获得false,请将其丢弃。 (如果有人知道防止不良“图像”出现的更好解决方案,我会全力以赴!)

      将图像存储在网络可访问目录之外的文件系统上。使用物理位置的密钥表是一个好主意。将IMAGETYPE_XXX 值与其一起存储(它是getimagesize() 返回的数组的第二个索引)并打开它以使用imagejpeg()imagepng() 等提供图像。不要忘记设置您的内容-type 标题。

      【讨论】:

      • 其实他可以readfile标题正确的图片,不需要重新加载图片。即使这种方法对于以不同分辨率(缩略图、压缩、完整图像等)提供图像或应用水印非常有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      • 2018-04-23
      • 1970-01-01
      • 2012-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多