首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 判斷上傳文件真實類型:魔數檢測防偽造 MIME、finfo 最佳實踐

     編輯:PHP 綜合知識

不可信任 $_FILES['file']['type'](瀏覽器根據副檔名猜測,易偽造)。必須讀取檔案頭部「魔數」或使用 finfo 擴展檢測真實 MIME 類型。

魔數對照表(常見格式)

格式魔數 (十六進制)ASCII 表示
JPEGFF D8 FFÿØÿ
PNG89 50 4E 47 0D 0A 1A 0A‰PNG···
GIF87a/89a47 49 46 38 37/39 61GIF87a / GIF89a
PDF25 50 44 46%PDF
ZIP / DOCX / XLSX / APK50 4B 03 04PK··
RAR52 61 72 21 1A 07Rar!··
BMP42 4DBM
MP3 (ID3)49 44 33ID3
WAV52 49 46 46RIFF
AVI52 49 46 46....41 56 49RIFF....AVI

方案一:finfo 擴展(推薦,PHP 內建)

function get_real_mime($filepath) {
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    return $finfo->file($filepath);
}

// 使用
$allowed = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$mime = get_real_mime($_FILES['file']['tmp_name']);
if (!in_array($mime, $allowed)) {
    die('檔案類型不允許: ' . $mime);
}
  • 優點:標準庫、支援上百種格式、自動更新魔數資料庫
  • 需求:PHP 編譯啟用 --with-mime-magic 或安裝 fileinfo 擴展

方案二:手動讀取魔數(無擴展依賴)

function get_mime_by_magic($filepath) {
    $fp = fopen($filepath, 'rb');
    $header = fread($fp, 8); // 讀前 8 字節足夠大多數格式
    fclose($fp);

    $hex = bin2hex($header);
    $signatures = [
        'ffd8ff' => 'image/jpeg',
        '89504e470d0a1a0a' => 'image/png',
        '474946383761' => 'image/gif',
        '474946383961' => 'image/gif',
        '25504446' => 'application/pdf',
        '504b0304' => 'application/zip', // 含 docx/xlsx
        '526172211a07' => 'application/x-rar-compressed',
        '424d' => 'image/bmp',
        '494433' => 'audio/mpeg',
        '52494646' => 'audio/wav', // 需進一步判斷 WAVE/AVI
    ];

    foreach ($signatures as $sig => $mime) {
        if (strpos($hex, $sig) === 0) return $mime;
    }
    return 'application/octet-stream';
}

方案三:Linux file 命令(CLI 環境)

function get_mime_by_file_cmd($filepath) {
    $out = shell_exec('file --brief --mime-type ' . escapeshellarg($filepath));
    return trim($out);
}

優點:系統級魔數庫最全;缺點:依賴外部命令、效能較差、Windows 不通用。

完整上傳驗證流程

function validate_upload($file, array $allowedMimes, int $maxSize = 10485760) {
    // 1. PHP 錯誤碼
    if ($file['error'] !== UPLOAD_ERR_OK) {
        return ['ok' => false, 'msg' => '上傳錯誤: ' . $file['error']];
    }
    // 2. 大小
    if ($file['size'] > $maxSize) {
        return ['ok' => false, 'msg' => '檔案過大'];
    }
    // 3. 真實 MIME
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $realMime = $finfo->file($file['tmp_name']);
    if (!in_array($realMime, $allowedMimes)) {
        return ['ok' => false, 'msg' => "類型不允許: $realMime"];
    }
    // 4. 副檔名一致性(可選)
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    $extMimes = [
        'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg',
        'png' => 'image/png', 'gif' => 'image/gif',
        'pdf' => 'application/pdf', 'zip' => 'application/zip',
    ];
    if (isset($extMimes[$ext]) && $extMimes[$ext] !== $realMime) {
        return ['ok' => false, 'msg' => '副檔名與內容不符'];
    }
    return ['ok' => true, 'mime' => $realMime];
}

進階:圖片二次渲染徹底清除隱藏代碼

function sanitize_image($src, $dest) {
    $info = getimagesize($src);
    if (!$info) return false;
    $mime = $info['mime'];
    $create = 'imagecreatefrom' . ($mime === 'image/jpeg' ? 'jpeg' : ($mime === 'image/png' ? 'png' : 'gif'));
    if (!function_exists($create)) return false;

    $img = $create($src);
    if (!$img) return false;

    $new = imagecreatetruecolor($info[0], $info[1]);
    if ($mime === 'image/png' || $mime === 'image/gif') {
        imagealphablending($new, false);
        imagesavealpha($new, true);
        $transparent = imagecolorallocatealpha($new, 0,0,0,127);
        imagefill($new, 0, 0, $transparent);
    }
    imagecopyresampled($new, $img, 0,0, 0,0, $info[0], $info[1], $info[0], $info[1]);

    $outFunc = 'image' . ($mime === 'image/jpeg' ? 'jpeg' : ($mime === 'image/png' ? 'png' : 'gif'));
    $result = $outFunc($new, $dest, 90);
    imagedestroy($img); imagedestroy($new);
    return $result;
}

將上傳圖片重新繪製保存,可剝離 EXIF、隱寫代碼、惡意腳本片段。

總結建議

  1. **生產環境必須啟用 fileinfo 擴展**,使用 finfo 為首選
  2. 結合**副檔名白名單 + MIME 白名單 + 魔數驗證**三重過濾
  3. 圖片類上傳**強制二次渲染**清除潛在威脅
  4. 儲存路徑**不可直接 Web 訪問**,下載時經 PHP 輸出並設 Content-Disposition: attachment
  5. 定期更新系統 /usr/share/misc/magic 魔數資料庫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved