PHP项目上传文件类型如何校验

wen PHP项目 19

本文目录导读:

PHP项目上传文件类型如何校验

  1. 前端校验(用户体验层,不可作为安全依据)
  2. 后端校验(安全核心)
  3. 安全建议
  4. 完整示例代码
  5. 重要提醒

在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();
}

重要提醒

  1. 永远不要信任 $_FILES['file']['type'],这个值由客户端提供,可以伪造
  2. 使用 finfo 函数获取文件的真实MIME类型
  3. 结合扩展名和MIME类型进行双重验证
  4. 对图片文件使用 getimagesize() 进行二次验证
  5. 存储文件时重新生成文件名,避免使用用户提供的文件名
  6. 设置文件上传大小限制(php.ini 中 upload_max_filesizepost_max_size

通过以上方法,可以有效地防止用户上传恶意文件,确保系统安全。

抱歉,评论功能暂时关闭!