用户
 找回密码
 入住 CI 中国社区
搜索
查看: 6861|回复: 4
收起左侧

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

[复制链接]
发表于 2012-4-2 16:19:16 | 显示全部楼层 |阅读模式
      已经有一段时间没用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复制代码
 
<?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]) > 0) // A return status code != 0 would mean failed execution
            {
                $this->file_type = rtrim($output[0]);
                return;
            }
        }
 
        $this->file_type = $file['type'];
    }
 
 
}
 
复制代码



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

发表于 2012-4-3 22:59:49 | 显示全部楼层
{:soso_e113:}
发表于 2012-4-4 13:31:43 | 显示全部楼层
楼主好 谢谢你提供的方法 我做这个测试 我上传的jpg 二进制 显示 的是474946 我有点担心 不知道文件类型二进制在win和linux等平台下 是否有差异 为什么在win下的jpg上传 会有不同的二进制标识
 楼主| 发表于 2012-4-4 15:24:47 | 显示全部楼层
我获取的非二进制,而是16进制头文件信息

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

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

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

本版积分规则