準確判斷檔案 MIME 類型對安全上傳、內容協商、檔案處理至關重要。本文比較三種主流方法的優缺點、安裝需求、適用場景與程式碼範例。
| 方法 | PHP 版本 | 擴展需求 | 支援檔案類型 | 準確度 | 推薦度 |
|---|---|---|---|---|---|
| mime_content_type() | < 5.3 | 無 (內建) | 所有 | 中 | ❌ 已棄用 |
| Fileinfo (finfo) | 5.3+ | fileinfo | 所有 | 高 | ✅ 官方推薦 |
| image_type_to_mime_type() | 4.3+ | exif + mbstring | 僅圖片 | 極高 | ✅ 圖片專用 |
<?php
// PHP 5.3+ 會產生 E_DEPRECATED 警告
$mime = mime_content_type('image.jpg');
echo $mime; // image/jpeg
// 依賴 magic.mime 檔案,準確度受系統資料庫影響
?>
# Ubuntu/Debian apt-get install php-fileinfo # 或編譯時加入 --enable-fileinfo # CentOS/RHEL yum install php-pecl-fileinfo # PHP 7.4+ 通常內建啟用 # Windows: php.ini 取消註解 extension=fileinfo
<?php
// 物件導向風格 (推薦)
$finfo = new finfo(FILEINFO_MIME_TYPE); // 僅回傳 MIME 類型
$mime = $finfo->file('document.pdf');
echo $mime; // application/pdf
// 程序風格
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, 'video.mp4');
finfo_close($finfo);
// 取得更多資訊
$finfo = new finfo(FILEINFO_MIME); // MIME + 編碼
$result = $finfo->file('test.txt'); // text/plain; charset=us-ascii
?>
# 需 exif 擴展 (通常需 mbstring) # php.ini extension=mbstring extension=exif # 注意:exif 必須在 mbstring 之後載入
<?php
// 1. 先用 exif_imagetype 偵測圖片類型常數
$imageType = exif_imagetype('photo.jpg'); // 回傳 IMAGETYPE_JPEG (2)
// 2. 轉換為 MIME 字串
$mime = image_type_to_mime_type($imageType);
echo $mime; // image/jpeg
// 常見圖片類型常數對照
$types = [
IMAGETYPE_GIF => 'image/gif',
IMAGETYPE_JPEG => 'image/jpeg',
IMAGETYPE_PNG => 'image/png',
IMAGETYPE_WEBP => 'image/webp',
IMAGETYPE_BMP => 'image/bmp',
IMAGETYPE_ICO => 'image/vnd.microsoft.icon',
];
?>
<?php
function validateUploadedFile($tmpPath, array $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']) {
// 1. Fileinfo 驗證 (主要防禦)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($tmpPath);
if (!in_array($mime, $allowedMimes, true)) {
throw new Exception("不允許的檔案類型: $mime");
}
// 2. 圖片專用二次驗證 (防禦深度)
if (str_starts_with($mime, 'image/')) {
$imageType = @exif_imagetype($tmpPath);
if ($imageType === false) {
throw new Exception("無效的圖片檔案");
}
$mime2 = image_type_to_mime_type($imageType);
if ($mime !== $mime2) {
throw new Exception("MIME 類型不一致,疑似偽裝檔案");
}
}
// 3. 可選:getimagesize() 驗證尺寸
$size = @getimagesize($tmpPath);
if ($size === false) {
throw new Exception("無法讀取圖片尺寸");
}
return [
'mime' => $mime,
'width' => $size[0],
'height' => $size[1],
'type' => $size[2] // IMAGETYPE_* 常數
];
}
?>
❌ 錯誤做法:根據副檔名判斷類型
$ext = pathinfo($file, PATHINFO_EXTENSION);
攻擊者可上傳shell.php.jpg或修改副檔名繞過檢查。
✅ 正確做法:內容檢測 (Fileinfo/exif) + 副檔名白名單 雙重驗證