攻击者可以通过将恶意脚本(如 PHP 代码)伪装成图片上传。例如,将一个包含 PHP 后门的脚本命名为 .jpg 或 .png,然后上传。若不使用 exif_imagetype() 检测实际文件类型,image_type_to_extension() 可能会错误地返回合法扩展名,从而误导后续逻辑,甚至将其保存在可执行目录中:
$filename = $_FILES['image']['tmp_name'];
$ext = image_type_to_extension(@exif_imagetype($filename));
// 错误用法:没有检测exif_imagetype返回值是否为false
$newName = uniqid() . $ext;
move_uploaded_file($filename, "/var/www/html/uploads/" . $newName);
如果上传的文件并非真实图片,例如一个伪造的 PHP 脚本,但扩展名是 .jpg,攻击者就可能通过访问 http://m66.net/uploads/xxxx.jpg 执行恶意代码。
当 exif_imagetype() 不能识别文件类型时,它会返回 false。如果此时仍将结果传入 image_type_to_extension(),会返回 .int(0),导致生成文件名混乱,甚至逻辑异常。例如:
$type = exif_imagetype($filename);
$ext = image_type_to_extension($type);
// 如果$type为false,$ext为"."
这样可能会导致程序使用错误的文件扩展名,影响后续操作,甚至造成资源泄露。
在某些服务器环境中,如果 PHP 没有启用 fileinfo 或 exif 扩展,那么 exif_imagetype() 会失效。若仍然依赖其结果并传入 image_type_to_extension(),程序容易崩溃或处理逻辑错误,影响用户体验。
有些服务部署安全策略是基于扩展名进行限制,比如只允许 .jpg、.png 文件被访问。如果攻击者伪造文件名,并绕过后端检查,服务器可能错误地授予访问权限。
例如,上传了一个名为 .php.jpg 的文件,但真实格式是 PHP。Nginx 在某些配置下会将其识别为 .php 文件而执行,从而产生严重后果:
<?php echo shell_exec($_GET['cmd']); ?>
然后通过访问:
http://m66.net/uploads/backdoor.php.jpg?cmd=ls
即可远程控制服务器。
为了避免上述风险,应该采取以下措施:
使用 exif_imagetype() 且判断返回值是否为 false
使用 finfo_file() 来进一步验证 MIME 类型
限制上传目录不可执行
不信任用户提交的扩展名
使用白名单验证 image_type_to_extension() 的返回值
示例代码:
$filename = $_FILES['image']['tmp_name'];
$imageType = @exif_imagetype($filename);
if ($imageType === false) {
die('不支持的图片格式');
}
$ext = image_type_to_extension($imageType, false);
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($ext, $allowed)) {
die('不允许的图片类型');
}
$newName = uniqid() . '.' . $ext;
move_uploaded_file($filename, '/var/www/uploads/' . $newName);