CodeIgniter遇到了“The filetype you are attempting to upload is not allowed.”的问题

1. 问题的引出

公司内部系统需要上传Word文档,开发用的是CI 2.1.4。

在开发和大部分人的机器上跑得都挺好的,就是有几个人说不能上传文件。查看了一下错误信息,就是:

CodeIgniter: “The filetype you are attempting to upload is not allowed.”

检查服务器端的代码:

    $config ['allowed_types'] = 'doc|docx’;

应该是支持doc和docx格式的文件上传的。

只好打出$_FILES来看看。分别在正常的机器和不正常的机器上尝试上传,发现上传文件的type不一样。

正常机器上传的时候,type是 “application/msword”,而不能正常上传的用户的这个值则为”application/kswps”。

原来如此。

2. 解决方法

解决方法就是修改应用根目录下的config/mimes.php文件,增加doc的Content-Type类型。

    'doc'     =>     array('application/msword','application/kswps'),

其中后面的’application/kswps’是我手工加的。

3. 谁设置了Content-Type

谁设置的mime type,根据什么设置的?

首先来看一下第一个问题的答案,那就是浏览器。
根据PHP的官方记载

$_FILES[‘userfile’][‘type’]
文件的 MIME 类型,如果浏览器提供此信息的话。一个例子是“image/gif”。不过此 MIME 类型在 PHP 端并不检查,因此不要想当然认为有这个值。

第二个问题,浏览器根据什么去设置上传文件的mime type(或者叫Content-Type)呢?

在网上查了下,只找到了Windows相关的内容,可能和注册表等相关,比如右键点击上传出错的文件,会发现打开类型用的就是WPS。

skitch

再往深处就没有去探究了。

4. 文件上传和安全性

由此问题(文件类型检查),顺便说说文件上传时相关的安全问题。文件上传所能引起的安全问题有很多,比如Dos,比如恶意病毒等。这里我们只简单的对如何防止用户上传非法文件作出说明。

所谓的非法文件,最简单的例子就是在上传头像的时候,用户传了个.php脚本文件上去,如果这个文件被保存在web的公开目录(即Web服务器的www_root)下的话,那么很可能就造成这个脚本在服务器上被执行的后果。

防止用户上传非法文件,最基本的预防措施主要有如下一些:

  1. 防止文件名中存在非法字符,比如..什么的,这时候最好在保存文件的时候,生成随机字符串文件名保存。
  2. 对文件扩展名和Content-Type进行检查

然而,只做第2步的话,还不能保证100%的安全性。我们还需要确认浏览器传过来的文件内容,确实是和它所声称的Content-Type和扩展名相一致。也就是说对文件内容做检查。

4.1. 上传文件为图像等时的简单解决方法

如果是图像的话还算简单,直接对上传的文件调用图形函数即可。如果出错,就可以认为用户上传的文件是非法文件。各种语言都有自己的方式,PHP的话可以使用getimagesize等函数。

对其它类型文件,比如doc或者pdf也可以调用API来实现读取标题,页数等方法确认文档合法性。

4.2. 更为通用的解决方法

更为通用的方法就是对上传文件进行Magic Numbers检查。

关于什么是Magic numbers可以参考wiki页面:http://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files

4.2.1. 使用PHP语言

比如PHP里可以使用Fileinfo (http://php.net/manual/en/function.finfo-file.php)。Fileinfo在5.3已经是标准发布的一部分了,在这之前都以PECL扩展的形式存在。下面的例子在官网文档也可以看到。

$file="/path/to/file";

// in PHP 4, we can do:
$fhandle= finfo_open(FILEINFO_MIME);
$mime_type= finfo_file($fhandle,$file);// e.g. gives "image/jpeg"

// in PHP 5, we can do:

$file_info=newfinfo(FILEINFO_MIME); // object oriented approach!
$mime_type=$file_info->buffer(file_get_contents($file)); // e.g. gives "image/jpeg"

switch($mime_type) {
case"image/jpeg":
// your actions go here...
}

如果不能使用finfo的话,那么也有其它方法,比如用file命令(注意跨平台问题)

下面代码则更通用一些,参考网址:http://stackoverflow.com/questions/134833/how-do-i-find-the-mime-type-of-a-file-with-php

function get_mime($file){
    if(function_exists("finfo_file")){
        // return mime type ala mimetype extension 
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $file); 
        finfo_close($finfo); 
        return $mime; 
    }elseif(function_exists("mime_content_type")){
        return mime_content_type($file); 
    }elseif(!stristr(ini_get("disable_functions"),"shell_exec")){ 
        // http://stackoverflow.com/a/134930/1593459 
        $file = escapeshellarg($file); 
        $mime = shell_exec("file -bi ". $file);
        return $mime;
    }else{ 
        return false; 
    }
}

4.2.2. 使用Java语言

Java的话也有很多选择,比如Java7的话可以使用java/nio/file/Files/Files的
probeContentType(http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#probeContentType%28java.nio.file.Path%29)方法,如果是Java7之前版本的话,则可以考虑下面要说的两个第三方库。

jmimemagic是一个在Java项目里使用判断文件或者输入流的MIME类型的类库。项目地址 https://github.com/arimus/jmimemagic

也可以使用 Apache Tika(http://tika.apache.org/ ) 。Tika主要是分析文档的元数据及文本抽取,一般都会和Solr配合进行全文索引。

如果不嫌Tika太重的话,应该是一个不错的选择。probeContentType的话貌似支持还不完善,据说还有Mac下的bug。

4.3. 展示上传文件的注意点

另外,在向客户端展示上传文件(显示或下载的时候),需要注意以下几点:

  1. 正确设置返回头的Content-Type
  2. 需要下载时,在响应头中指定Content-Disposition:attachment

— 全文完 —

参考资料:

1. 文件签名一览:http://www.garykessler.net/library/file_sigs.html

2. Smart File Type Detection Using PHP: http://designshack.net/articles/php-articles/smart-file-type-detection-using-php/

3. CodeIgniter: “The filetype you are attempting to upload is not allowed.”: http://ellislab.com/forums/viewthread/213660/

4.Problem Uploading Mp3 and Video files: http://ellislab.com/forums/viewthread/75310/



Posted in Tech, Web Tagged with: , , ,

无觅相关文章插件,快速提升流量