【问题标题】:Secure way to upload image in php and htaccess在 php 和 htaccess 中上传图像的安全方法
【发布时间】:2013-01-21 08:02:11
【问题描述】:

我在互联网上找到了以下代码,用于在 php 中安全上传图片。

我想知道它涵盖了图片上传中所有可能的攻击方式。

define('MAX_SIZE_EXCEDED', 101);
define('UPLOAD_FAILED', 102);
define('NO_UPLOAD', 103);
define('NOT_IMAGE', 104);
define('INVALID_IMAGE', 105);
define('NONEXISTANT_PATH', 106);

class ImgUploader
{
  var $tmp_name;
  var $name;
  var $size;
  var $type;
  var $error;
  var $width_orig;
  var $height_orig;
  var $num_type;
  var $errorCode = 0;
    var $allow_types = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);

  function __construct($fileArray)
  {
    foreach($fileArray as $key => $value)
    {
      $this->$key = $value;
    }
    if($this->error > 0)
    {
      switch ($this->error)
      {
        case 1: $this->errorCode = MAX_SIZE_EXCEDED; break;
        case 2: $this->errorCode = MAX_SIZE_EXCEDED; break;
        case 3: $this->errorCode = UPLOAD_FAILED; break;
        case 4: $this->errorCode = NO_UPLOAD; break;
      }
    }
    if($this->errorCode == 0)
    {
      $this->secure();
    }
  }

  function secure()
  {
    //$this->num_type = exif_imagetype($this->tmp_name);
    @list($this->width_orig, $this->height_orig, $this->num_type) = getimagesize($this->tmp_name);

    if(filesize($this->tmp_name) > 1024*1024*1024*5) // allows for five megabytes.  Change this number if need be.
    {
      $this->errorCode = MAX_SIZE_EXCEDED;
      return false;
    }

    if (!$this->num_type)
    {
      $this->errorCode = NOT_IMAGE;
        return false;
    }
    if(!in_array($this->num_type, $this->allow_types))
    {
      $this->errorCode = INVALID_IMAGE;
      return false;
    }
  }

  function getError()
  {
    return $this->errorCode;
  }

  function upload_unscaled($folder, $name)
  {
    return $this->upload($folder, $name, "0", "0");
  }

  function upload($folder, $name, $width, $height, $scaleUp = false)
  {
    // $folder is location to be saved
    // $name is name of file, without file extention
    // $width is desired max width
    // $height is desired max height

    if($this->errorCode > 0)
      return false;

    // deal with sizing
    // if image is small enough to not scale, or upload_unscaled() is called, don't scale
    if((!$scaleUp && ($width > $this->width_orig && $height > $this->height_orig)) || ($width === "0" && $height === "0"))
    {
      $width = $this->width_orig;
      $height = $this->height_orig;
    }
    else
    {
      // if height diff is less than width dif, calc height
      if(($this->height_orig - $height) <= ($this->width_orig - $width))
        $height = ($width / $this->width_orig) * $this->height_orig;
      else
        $width = ($height / $this->height_orig) * $this->width_orig;
    }

    // Resample
    switch($this->num_type)
    {
      case IMAGETYPE_GIF: $image_o = imagecreatefromgif($this->tmp_name); $ext = '.gif'; break;
      case IMAGETYPE_JPEG: $image_o = imagecreatefromjpeg($this->tmp_name); $ext = '.jpg'; break;
      case IMAGETYPE_PNG: $image_o = imagecreatefrompng($this->tmp_name); $ext = '.png'; break;
    }

    $filepath = $folder.(substr($folder,-1) != '/' ? '/' : '');
    if(is_dir($_SERVER['DOCUMENT_ROOT'].$filepath))
      $filepath .= $name.$ext;
    else
    {
      $this->errorCode = NONEXISTANT_PATH;
      imagedestroy($image_o);
      return false;
    }

    $image_r = imagecreatetruecolor($width, $height);
    imagecopyresampled($image_r, $image_o, 0, 0, 0, 0, $width, $height, $this->width_orig, $this->height_orig);

    switch($this->num_type)
    {
      case IMAGETYPE_GIF: imagegif($image_r, $_SERVER['DOCUMENT_ROOT'].$filepath); break;
      case IMAGETYPE_JPEG: imagejpeg($image_r, $_SERVER['DOCUMENT_ROOT'].$filepath); break;
      case IMAGETYPE_PNG: imagepng($image_r, $_SERVER['DOCUMENT_ROOT'].$filepath); break;
    }

    imagedestroy($image_o);
    imagedestroy($image_r);

    return '/'.$filepath;
  }
}

我还在“images”文件夹中存储了一个 .htaccess 文件,该文件会关闭文件脚本,因此没有人可以执行照片文件夹中的脚本。

<Files ^(*.jpg)>
order deny,allow
deny from all
</Files>
Options -Indexes
Options -ExecCGI 
AddHandler cgi-script .php .php3 .php4 .php5 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi

出于安全原因,这样做就足够了,或者我必须采取一些其他措施来保护我的代码和网站。

【问题讨论】:

  • 你自己试过这个代码吗?你有使用示例吗?

标签: php image-uploading


【解决方案1】:

我要说的第一件事是用 Apache 的 mod_mime 检查文件类型,否则我可以发送一个 HTTP 请求,上面写着 I'm a JPEG 文件!,您只需信任像 这样的文件扩展名是的!你有 .jpg 扩展名,但实际上我可以注入 PHP 源代码而不是 JPEG 数据,然后我可以在你的服务器上执行 PHP 源代码。

另一件事是始终强制 Apache 不运行任何这些图像类型。您可以使用 ForceType 指令来做到这一点。

<FilesMatch "(?i)\.jpe?g$">
    ForceType image/jpeg
</FilesMatch>

编辑:

我没有看到底线。关闭文件脚本绝对有帮助。

【讨论】:

    【解决方案2】:

    此代码安全吗?

    PHP

    简而言之,不,不一定!有一个致命的缺陷,代码没有覆盖,但不要绝望,这很容易解决!

    虽然脚本正在测试文件的扩展名,但它并未测试文件的 MIME 类型,因此可能会引发一系列问题。

    除了这个小漏洞之外,代码非常安全。

    阿帕奇

    伪造 HTTP 请求看起来像 JPEG 是一件非常容易的事情,虽然 PHP 可以处理这个问题,但有时最好确保这个漏洞已经被各个角度覆盖。使用 Apache 中的 MOD_MIME 模块的简单修复可用于修复此缺陷,如下所述。

    HTTPS - 可能无关

    如果您担心这些文件会在客户端和服务器之间被嗅探,那么绝对重要的安全部分就是在您的网站上使用 SSL 证书。这将加密客户端和服务器之间的所有数据。

    但是,如果您只是担心服务器端的事情,尽管建议这样做,但这并不是必需的。

    可能的解决方案

    finfo() 函数

    finfo() 函数将返回文件的 MIME 类型,因此指示它是否为 JPEG 并且可以上传。

    See Here 了解有关此功能的更多详细信息。

    上传备选

    就个人而言,我更喜欢使用 Colin Verot 上传类。这个类非常易于使用,涵盖了所有潜在的安全问题,使用 GD 库具有大量扩展功能,并且会不断维护。

    访问 Colin Verot 的网站 here 下载并开始使用该课程。

    Apache MOD_MIME

    Apache MOD_MIME 模块将强制检查发送到服务器的文件的 MIME 类型。

    查看here了解更多信息。

    【讨论】:

      【解决方案3】:

      代码看起来不错,最重要的答案也很好我只想再补充一点关于安全性:

      • 您应该将文件存储在文档根目录之外的目录中(不能通过 http 请求访问),并通过首先检查正确授权的脚本来提供它们。

      【讨论】:

        【解决方案4】:

        由于您关闭了服务器上所有文件的解释,剩下的就是文件名。如果您生成自己的名称或过滤用户提供的名称,则无需担心。

        嗯.. 几乎没有。 GD 库中可能存在安全错误,因此使用恶意图像可能会在调整大小时做坏事。但这不是您可以通过 PHP 处理的,因此请保持服务器更新。

        【讨论】:

          【解决方案5】:

          如果你不介意使用现有的 PHP 上传系统,这个 jQuery/PHP 上传框架非常可配置且令人惊叹。

          http://blueimp.github.com/jQuery-File-Upload/

          【讨论】:

          • 据我所知,该表单(在整个网络上使用)要求用户实现自己的安全性。它看起来很可爱,但任何体面的开发人员都可以使用 jquery。首要任务始终是安全。
          • 该框架包括易于配置的文件类型限制,并且路径非常灵活,允许您将文件存储在任何受限制的目录中,甚至是不在Web根目录中的目录。这简化了安全文件上传的大部分工作。
          猜你喜欢
          • 1970-01-01
          • 2012-10-25
          • 2013-07-09
          • 1970-01-01
          • 1970-01-01
          • 2012-01-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多