文件上傳漏洞如幽靈般伴隨動態 Web 發展至今,頻發於商城、論壇、網盤、CMS。根因在於**上傳過程中多個關鍵屬性 (文件名、類型、路徑) 均源自客戶端可控輸入**,開發者錯誤信任並直接使用。本文梳理上傳流程、客戶端可控點、三大經典漏洞類型,為後續深入分析奠基。
POST /upload.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXYZ
...
------WebKitFormBoundaryXYZ
Content-Disposition: form-data; name="file"; filename="shell.php.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryXYZ--
服務端 `$_FILES['file']` 結構:
| 鍵名 | 來源 | 可信度 | 說明 |
|---|---|---|---|
| name | 客戶端 `filename=""` | ❌ 完全可控 | 原始文件名,含路徑、擴展名、特殊字符 |
| type | 客戶端 `Content-Type:` | ❌ 完全可控 | 瀏覽器按擴展名猜測,可任意偽造 |
| tmp_name | 服務端臨時文件 | ✅ 可信 | PHP 生成的隨機臨時文件路徑 |
| size | 服務端計算 | ✅ 可信 | 臨時文件大小 (字節) |
| error | 服務端判定 | ✅ 可信 | 上傳錯誤碼 (UPLOAD_ERR_OK=0 等) |
許多開發者認為 `$_FILES['file']['type']` 是 PHP 自動分析文件內容得出的 MIME 類型。**錯誤!** 它直接來自客戶端請求頭 `Content-Type`,攻擊者用 Burp Suite 修改為 `image/jpeg` 即可繞過 `if ($type === 'image/jpeg')` 判斷。
4274242742:文件名檢測繞過深度剖析與修復42743:目錄遍歷與 Null Byte 繞過 include/讀取任意文件42745:安全文件上傳完整實現代碼