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

PHP in_array 檢查文件類型失效:$_FILES 數組結構誤解導致的典型坑

     編輯:程式設計綜合問答

問題代碼


$tp = array("image/gif","image/pjpeg","image/jpeg","image/png" );

if( ! in_array($_FILES["pictures"]["type"][$key], $tp)) {
    echo "";
}

現象:上傳 MP3 時,有的文件名能觸發彈窗,有的(如 `-The Girl Is Mine Ft Brandy Monica (Remix).mp3`)卻不彈窗,檢測失效。

根本原因:$_FILES 結構誤解

$_FILES["pictures"]["type"][$key] 這種寫法僅適用於多文件上傳且 input name 為數組形式 (`name="pictures[]"`) 時。

單文件上傳 (``)


// $_FILES 結構
$_FILES["pictures"] = [
    "name"     => "test.mp3",
    "type"     => "audio/mpeg",  // 字符串,非數組!
    "tmp_name" => "/tmp/phpXXXX",
    "error"    => 0,
    "size"     => 12345
];
// 正確訪問:$_FILES["pictures"]["type"]

多文件上傳 (``)


// $_FILES 結構
$_FILES["pictures"] = [
    "name"     => ["a.mp3", "b.jpg"],
    "type"     => ["audio/mpeg", "image/jpeg"],  // 數組
    "tmp_name" => ["/tmp/php1", "/tmp/php2"],
    "error"    => [0, 0],
    "size"     => [123, 456]
];
// 正確訪問:$_FILES["pictures"]["type"][0], $_FILES["pictures"]["type"][1]

為什麼文件名影響判斷?

瀏覽器根據文件擴展名猜測 MIME 類型填入 `type` 字段:

  • `Family Conference.mp3` → 瀏覽器識別 `.mp3` → `type="audio/mpeg"` → 不在 `$tp` (圖片類型) 中 → 觸發錯誤彈窗。
  • `-The Girl Is Mine Ft Brandy Monica (Remix).mp3` → 瀏覽器**可能識別失敗**(特殊字符、過長、無擴展名關聯)→ `type=""` (空字符串) 或 `application/octet-stream` → 空字符串不在 $tp 中,但 `in_array("", $tp)` 為 false,`!false` 為 true,理應觸發彈窗?

關鍵點:代碼中 `$key` 變量來源不明。若單文件上傳但代碼按多文件寫法 `$_FILES["pictures"]["type"][$key]`,當 `$key=0` 時:

  • 字符串 `"audio/mpeg"[0]` → `'a'` (字符串按索引取字符) → `'a'` 不在 `$tp` → 觸發彈窗。
  • 字符串 `""[0]` → 空/報錯 → 行為不可預期。

這解釋了「有的彈窗有的不彈窗」的隨機性:取決於 `$key` 值和 `type` 字符串首字符是否碰巧匹配數組值。

正確的檢查代碼模板


$allowedTypes = ["image/gif", "image/jpeg", "image/png", "image/pjpeg"]; // 建議統一小寫

// 兼容單文件/多文件寫法
$files = $_FILES["pictures"];
$isMultiple = is_array($files["name"]);

$indices = $isMultiple ? array_keys($files["name"]) : [null];

foreach ($indices as $key) {
    $type = $isMultiple ? $files["type"][$key] : $files["type"];
    $name = $isMultiple ? $files["name"][$key] : $files["name"];
    $error = $isMultiple ? $files["error"][$key] : $files["error"];
    
    if ($error !== UPLOAD_ERR_OK) continue; // 跳過錯誤文件
    
    // 核心修正:嚴格類型比較,轉小寫
    if (!in_array(strtolower($type), $allowedTypes, true)) {
        // 記錄日誌、返回錯誤、拒絕上傳
        log_upload_rejection($name, $type, "類型不在白名單");
        continue;
    }
    
    // 進一步驗證:文件內容檢測 (finfo)
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $realType = finfo_file($finfo, $files["tmp_name"][$key] ?? $files["tmp_name"]);
    finfo_close($finfo);
    
    if (!in_array($realType, $allowedTypes, true)) {
        log_upload_rejection($name, $realType, "內容類型偽裝");
        continue;
    }
    
    // 移動文件...
}

安全最佳實踐

  1. 不信任 $_FILES['type']:客戶端可偽造。必須用 `finfo_file()` 或 `mime_content_type()` 檢測文件實際內容。
  2. 白名單機制:僅允許業務必需的 MIME 類型。
  3. 文件名處理:存儲時使用隨機名 (UUID/哈希) + 原始擴展名,避免特殊字符、路徑遍歷、覆蓋風險。
  4. 存儲隔離:上傳目錄設置為不可執行腳本 (Nginx/Apache 配置 `location ~ \.php$ { deny all; }`)。
  5. 大小限制:PHP `upload_max_filesize`、`post_max_size` + 代碼層二次校驗。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved