本文目录导读:

在PHP项目中,文件上传类型校验需要从前端和后端两个层面进行,并特别注意安全性,以下是几种常用的方法及其最佳实践。
前端校验(用户体验层,不可作为安全依据)
HTML accept 属性
<input type="file" name="file" accept=".jpg,.jpeg,.png,.gif,.pdf" />
或使用MIME类型:
<input type="file" accept="image/jpeg,image/png,application/pdf" />
JavaScript 校验
function validateFileType() {
const file = document.getElementById('fileInput').files[0];
const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!allowedTypes.includes(file.type)) {
alert('不支持的文件类型');
return false;
}
return true;
}
后端校验(安全核心)
方法1:基于扩展名校验
<?php
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$fileExtension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($fileExtension, $allowedExtensions)) {
die('不允许的文件类型');
}
方法2:基于MIME类型校验(推荐)
<?php
$allowedMimes = [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf'
];
$fileMimeType = $_FILES['file']['type']; // 不可信,客户端可伪造
// 使用finfo函数获取真实的MIME类型(推荐)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$realMimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($realMimeType, $allowedMimes)) {
die('不允许的文件类型');
}
方法3:多维度校验(最安全)
<?php
function validateFile($file) {
// 允许的扩展名和MIME类型映射
$allowedTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
];
// 1. 检查扩展名
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!array_key_exists($extension, $allowedTypes)) {
return false;
}
// 2. 检查真实MIME类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$realMimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if ($realMimeType !== $allowedTypes[$extension]) {
return false;
}
// 3. 可选:对图片进行二次验证
if (strpos($realMimeType, 'image/') === 0) {
$imageInfo = getimagesize($file['tmp_name']);
if ($imageInfo === false) {
return false;
}
}
return true;
}
// 使用示例
if (!validateFile($_FILES['file'])) {
die('文件类型校验失败');
}
安全建议
文件类型与扩展名一致校验
// 检查扩展名和MIME类型是否匹配
$mimeToExtension = [
'image/jpeg' => ['jpg', 'jpeg'],
'image/png' => ['png'],
'image/gif' => ['gif'],
'application/pdf' => ['pdf']
];
头检测
// 读取文件头部字节判断真实类型
$fileContent = file_get_contents($_FILES['file']['tmp_name'], false, null, 0, 20);
// PDF文件头部: %PDF
if (strpos($fileContent, '%PDF') === 0) {
// 可能是PDF
}
// JPEG文件头部: FFD8FF
// PNG文件头部: 89504E47
使用第三方库
// 使用 composer 安装
// composer require guzzlehttp/psr7
use Psr\Http\Message\UploadedFileInterface;
function validateUploadedFile(UploadedFileInterface $uploadedFile) {
$allowedMimeTypes = ['image/jpeg', 'image/png'];
return in_array($uploadedFile->getClientMediaType(), $allowedMimeTypes);
}
完整示例代码
<?php
class FileUploadValidator {
private $allowedTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'pdf' => 'application/pdf'
];
public function validate($file) {
// 检查上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new Exception('文件上传失败');
}
// 检查文件大小(可选)
$maxSize = 5 * 1024 * 1024; // 5MB
if ($file['size'] > $maxSize) {
throw new Exception('文件大小超过限制');
}
// 检查扩展名
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!array_key_exists($extension, $this->allowedTypes)) {
throw new Exception('不允许的文件扩展名');
}
// 检查真实MIME类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if ($mimeType !== $this->allowedTypes[$extension]) {
throw new Exception('文件类型与扩展名不匹配');
}
// 对图片进行额外验证
if (strpos($mimeType, 'image/') === 0) {
if (@getimagesize($file['tmp_name']) === false) {
throw new Exception('图片文件损坏');
}
}
return true;
}
}
// 使用示例
$validator = new FileUploadValidator();
try {
$validator->validate($_FILES['file']);
// 文件类型有效,继续处理...
} catch (Exception $e) {
echo '错误:' . $e->getMessage();
}
重要提醒
- 永远不要信任
$_FILES['file']['type'],这个值由客户端提供,可以伪造 - 使用
finfo函数获取文件的真实MIME类型 - 结合扩展名和MIME类型进行双重验证
- 对图片文件使用
getimagesize()进行二次验证 - 存储文件时重新生成文件名,避免使用用户提供的文件名
- 设置文件上传大小限制(php.ini 中
upload_max_filesize和post_max_size)
通过以上方法,可以有效地防止用户上传恶意文件,确保系统安全。