loveinlove 发表于 2012-4-2 16:19:16

通过swfupload/其他flash上传控件 -上传实现严谨验证文件类型

      已经有一段时间没用swfupload,一来项目不需要实现多文件。二来想实现swfupload显示上传进度条,触发完成后的节点操作需要较严谨的js语句,且美化起来破繁。 当然利用swfupload上传不乏有效果美观的,如douban的swfupload照片上传、bbsmax的swfupload上传、QQ邮箱类swfupload的附件上传,都很不错。我也曾把douban的获取下来做了些应用。

      大家都知道通过flash上传的文件,mime 类型都被擦除 ,只能通过后缀判断文件的正确与否。从应用上来说无什么大影响。包括qq等大型应用,都是不管的,只判断后缀。

      但是总是会有捣乱的,伪后缀上传文件。比如图片不是图片,压缩文件不是压缩文件。非图片的img后缀文件上传后,img标签一包含就显示一个大叉叉很不美观。并且安全上也会大打折扣。

在网上闲逛时发现了几篇文章
http://blog.csdn.net/piaolankeke/article/details/5873156
http://www.21andy.com/blog/20090624/1337.html
通过文件头信息判断文件的真实信息。如此不管mime类型被伪造了,被擦除了都无碍。

想知道更多,可以看看文件头的相关文章。

如此我们就能用来扩展swfupload上传了,实现在无mime类型的情况下严谨的验证文件类型

代码如下,在application/libraries下扩展CI_uoload的 _file_mime_type 方法


<?php
class MY_Upload extends CI_Upload
{
    public $swf_mime = array(
               "FFD8FF"      =>    "image/jpeg",
               "89504E47"    =>    "image/png",
               "47494638"    =>    "image/gif",
               "424D"      =>    "image/bmp",
               "3C3F786D6C"=>    "text/xml",
               "504B0304"    =>    "application/x-zip");

    public function __construct()
    {
      parent::__construct();
    }

    protected function _file_mime_type($file)
    {
      if(file_exists($file['tmp_name']))
      {
            //$this->file_type = 'application/octet-stream';
            $check_file = @fopen($file['tmp_name'],'rb');
            $bin = fread($check_file,15);
            fclose($check_file);

            foreach ($this->swf_mime as $bin_name => $mime_type)
            {
                $blen=strlen(pack("H*",$bin_name)); //得到文件头标记字节数
                $tbin=substr($bin,0,intval($blen)); ///需要比较文件头长度

                if(strtolower($bin_name)==strtolower(array_shift(unpack("H*",$tbin))))
                {
               $this->file_type = $mime_type;
               return;
                }
            }
      }
      // Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag)
      if ( (float) substr(phpversion(), 0, 3) >= 5.3 && function_exists('finfo_file'))
      {
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system
            {
                $file_type = $finfo->file($file['tmp_name']);

                /* According to the comments section of the PHP manual page,
               * it is possible that this function returns an empty string
               * for some files (e.g. if they don't exist in the magic MIME database)
               */
                if (strlen($file_type) > 1)
                {
                  $this->file_type = $file_type;
                  return;
                }
            }
      }

      // Fall back to the deprecated mime_content_type(), if available
      if (function_exists('mime_content_type'))
      {
            $this->file_type = @mime_content_type($file['tmp_name']);
            return;
      }

      /* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type,
         * which is still more secure than depending on the value of $_FILES[$field]['type'].
         *
         * Notes:
         *    - a 'W' in the substr() expression bellow, would mean that we're using Windows
         *    - many system admins would disable the exec() function due to security concerns, hence the function_exists() check
         */
      if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec'))
      {
            $output = array();
            @exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code);
            if ($return_code === 0 && strlen($output) > 0) // A return status code != 0 would mean failed execution
            {
                $this->file_type = rtrim($output);
                return;
            }
      }

      $this->file_type = $file['type'];
    }


}



1,代码在203,210上均验证通过可用。只是2.1.0 的upload原本就有问题。可以确准好本身逻辑无错再做替换验证。
2,另外网络上流传的几个文件头信息,都不是很准,各有各的说法,要准还需要自己通过软件多看看各类的文件头。
3,以上代码修改了 _file_mime_type 方法,删除了对mime的判断。

sdink 发表于 2012-4-3 22:59:49

{:soso_e113:}

小贝 发表于 2012-4-4 13:31:43

楼主好 谢谢你提供的方法 我做这个测试 我上传的jpg 二进制 显示 的是474946 我有点担心 不知道文件类型二进制在win和linux等平台下 是否有差异 为什么在win下的jpg上传 会有不同的二进制标识

loveinlove 发表于 2012-4-4 15:24:47

我获取的非二进制,而是16进制头文件信息

你可以使用Ultraedit32 或者winhex 查看文件信息是否相同。

这应该不存在平台差异,我是在 类unix平台上测试的。

但是貌似个别文件的头文件信息不尽相同,具体的需要搜集相关资料,进行阅读。

Aloghli 发表于 2014-10-18 23:02:30

{:1_1:}
页: [1]
查看完整版本: 通过swfupload/其他flash上传控件 -上传实现严谨验证文件类型