文件魔数是文件头的十六进制标识,用于准确识别文件类型,如JPG为FFD8FF、PNG为89504E47;PHP可通过fread读取文件前16字节并转换为十六进制,再与标准魔数比对,结合finfo获取MIME类型、限制目录执行权限及重命名文件等措施,可有效防止恶意文件上传。
上传文件时仅靠扩展名判断类型很容易被绕过,攻击者可以将恶意脚本伪装成图片或其他安全格式。为提高安全性,PHP应通过读取文件的“魔数”(即文件头信息)来识别文件的真实类型。
文件魔数是文件开头的一段十六进制数据,用于标识文件的真实格式。例如:
这些数据无法通过简单修改后缀名改变,因此更可靠。
可以通过 fread() 读取文件前几个字节,并与已知魔数比对:
function checkFileMimeType($filePath) {
$handle = fopen($filePath, 'rb');
if (!$handle) {
return false;
}
// 读取前16个字节
$bin = fread($handle, 16);
fclose($handle);
$hexValues = unpack('H*', $bin);
$hex = strtoupper($hexValues[1]);
// 提取前几位进行匹配
$header = substr($hex, 0, 8);
if (strpos($header, 'FFD8FF') === 0) {
return 'image/jpeg';
} elseif (strpos($header, '89504E47') === 0) {
return 'image/png';
} elseif (strpos($header, '47494638') === 0) {
return 'image/gif';
} elseif (strpos($header, '25504446') === 0) {
return 'application/pdf';
}
// 可继续添加其他类型...
return 'unknown';
}
// 使用示例
$uploadedFile = $_FILES['file']['tmp_name'];
$mimeType = checkFileMimeType($uploadedFile);
if ($mimeType === 'image/jpeg' || $mimeType === 'image/png') {
// 允许上传
} else {
die('文件类型不合法!');
}
建议组合以下方式提升安全性:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);
某些图像编辑软件可能在文件头部添加额外数据(如元信息),影响读取。建议多读取一些字节并跳过常见干扰位,或结合多种方法交叉验证。
不要完全依赖 $_FILES['type'],该值由
客户端提供,极易伪造。
基本上就这些,魔数检测虽不能百分百防御所有伪装,但能有效拦截大多数基础攻击手段。配合其他措施,可大幅提升文件上传的安全性。