检测上传的文件类型

在$_FILES数组中,type字段中的图片类型不能作为检测的途径。应为这个值是浏览器通过HTTP头发送过来的,是可以伪造的。例如:

首先在test.php中写入,然后修改文件名为test.jpg,并通过表单添加该文件。
服务端接收的数据如下:

1
2
3
4
5
6
7
8
9
array (
'pic' => array (
'name' => 'test.jpg',
'type' => 'image/jpeg',
'tmp_name' => '/tmp/phpogAh98',
'error' => 0,
'size' => 0,
),
)

网络上也有一些通过读取文件前2字节,来判断文件类型的代码。但这些方法既不严谨也不够安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function file_type($filename)  
{
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch ($typeCode)
{
case 7790: $fileType = 'exe';break;
case 7784: $fileType = 'midi';break;
case 8297: $fileType = 'rar';break;
case 8075: $fileType = 'zip';break;
case 255216: $fileType = 'jpg';break;
case 7173: $fileType = 'gif';break;
case 6677: $fileType = 'bmp';break;
case 13780: $fileType = 'png';break;
default: $fileType = 'unknown: '.$typeCode;
}
//Fix
if ($strInfo['chars1']=='-1' AND $strInfo['chars2']=='-40' ) return 'jpg';
if ($strInfo['chars1']=='-119' AND $strInfo['chars2']=='80' ) return 'png';
return $fileType;
}

其实,在php中就有检测图片类型的方法。getimagesize。这个函数不需要GD等画图库的支持。打开文件失败或者不是有效的图片时返回false。

1
2
3
4
5
6
7
8
9
array (
0 => 460,
1 => 406,
2 => 2,
3 => 'width="460" height="406"',
'bits' => 8,
'channels' => 3,
'mime' => 'image/jpeg',
)

正确的放回值中mime亦可用作进一步过滤图片类型。另外需要注意,当被检测的文件为空时,会产生警告:

1
2
Notice: getimagesize(): Read error! in /fileupload/sample5/file_type_detect.php on line 2
false

可以使用filezie()函数先过滤掉空的文件。或者使用@getimagesize()抑制错误输出。