瀏覽器上傳檔案時會依副檔名與檔案簽名判斷 MIME 類型,不同瀏覽器對同一檔案回報的 MIME 可能不同。後端驗證時務必以「白名單 + 檔案簽名二次檢查」為準,不可單信 $_FILES['file']['type']。
| ID | 副檔名 | PHP 接收 MIME (IE / Firefox 多數一致) |
|---|---|---|
| 0 | gif | image/gif |
| 1 | jpg / jpeg | image/pjpeg (IE) / image/jpeg (FF) |
| 2 | png | image/x-png (IE) / image/png (FF) |
| 3 | bmp | image/bmp |
| 4 | psd | application/octet-stream |
| 5 | ico | image/x-icon |
| 6 | rar | application/octet-stream |
| 7 | zip | application/x-zip-compressed |
| 8 | 7z | application/octet-stream |
| 9 | exe | application/octet-stream |
| 10 | avi | video/avi |
| 11 | rmvb | application/vnd.rn-realmedia-vbr |
| 12 | 3gp | application/octet-stream |
| 13 | flv | application/octet-stream |
| 14 | mp3 | audio/mpeg |
| 15 | wav | audio/wav |
| 16 | krc | application/octet-stream |
| 17 | lrc | application/octet-stream |
| 18 | txt | text/plain |
| 19 | doc | application/msword |
| 20 | xls | application/vnd.ms-excel |
| 21 | ppt | application/vnd.ms-powerpoint |
| 22 | application/pdf | |
| 23 | chm | application/octet-stream |
| 24 | mdb | application/msaccess |
| 25 | sql | text/plain |
| 26 | con | application/octet-stream |
| 27 | log | text/plain |
| 28 | dat | text/plain |
| 29 | ini | application/octet-stream |
| 30 | php | application/octet-stream |
| 31 | html | text/html |
| 32 | htm | text/html |
function validateUpload($file, array $allowedMimes, array $allowedExts) {
// 1. 檢查上傳錯誤碼
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new Exception('上傳失敗,錯誤碼: ' . $file['error']);
}
// 2. 副檔名白名單(小寫比對)
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExts)) {
throw new Exception('副檔名不允許: ' . $ext);
}
// 3. MIME 白名單(參考值,非絕對)
if (!in_array($file['type'], $allowedMimes)) {
// 可選:僅記錄警告,不阻擋
error_log("MIME 不符: {$file['type']} vs 允許列表");
}
// 4. **關鍵:檔案簽名驗證**
$finfo = new finfo(FILEINFO_MIME_TYPE);
$realMime = $finfo->file($file['tmp_name']);
if (!in_array($realMime, $allowedMimes)) {
throw new Exception("檔案內容類型不符: $realMime");
}
// 5. 尺寸限制
if ($file['size'] > 10 * 1024 * 1024) { // 10MB
throw new Exception('檔案過大');
}
return true;
}
// 使用範例:僅允許圖片
$allowedMimes = ['image/gif', 'image/jpeg', 'image/png', 'image/webp'];
$allowedExts = ['gif', 'jpg', 'jpeg', 'png', 'webp'];
validateUpload($_FILES['avatar'], $allowedMimes, $allowedExts);
location ~ \.php$ { deny all; })。clamscan 或雲端 API。post_max_size/upload_max_filesize 限制。$_FILES['type']:瀏覽器僅依副檔名猜測,極易偽造。shell.php.jpg 可能繞過。$_FILES['error']:部分錯誤碼(如 UPLOAD_ERR_PARTIAL)仍會產生暫存檔。