文件上傳表單必須設置 enctype="multipart/form-data" 並使用 POST 方法:
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="100000">
<input name="userfile" type="file">
<input type="submit" value="上傳文件">
</form>
MAX_FILE_SIZE:客戶端提示上限 (字節),瀏覽器可能攔截超大文件,不可作為服務端唯一驗證依據。name="userfile":對應 $_FILES['userfile'] 數組鍵名。upload.php)<?php
function uploadFile($type, $name, $ext, $size, $error, $tmp_name, $targetName, $uploadDir) {
// 1. 基礎配置
$maxSize = 2000000; // 2MB
$allowedMimes = ['image/pjpeg', 'image/jpeg', 'image/jpg', 'image/gif', 'image/png'];
$allowedExts = ['.jpg', '.gif', '.png', '.jpeg']; // 去重
$filePath = $uploadDir . $targetName;
// 2. 目錄準備 (遞歸創建 + 權限設置)
if (!is_dir($uploadDir)) {
if (!mkdir($uploadDir, 0755, true)) {
die("無法創建上傳目錄: $uploadDir");
}
}
// 3. 文件大小驗證
if ($size > $maxSize) die("文件大小超過限制 ($maxSize 字節)");
if ($size == 0) die("請選擇文件");
// 4. 類型雙重驗證 (MIME + 擴展名)
if (!in_array($type, $allowedMimes) || !in_array(strtolower($ext), $allowedExts)) {
die("文件類型不允許,僅支持: " . implode(", ", $allowedExts));
}
// 5. 移動臨時文件
if (!move_uploaded_file($tmp_name, $filePath)) {
die("文件移動失敗,請檢查目錄寫入權限");
}
// 6. 錯誤碼處理 (作為最後防線)
switch ($error) {
case UPLOAD_ERR_OK: return true;
case UPLOAD_ERR_INI_SIZE: die("超過 php.ini upload_max_filesize");
case UPLOAD_ERR_FORM_SIZE: die("超過表單 MAX_FILE_SIZE");
case UPLOAD_ERR_PARTIAL: die("文件僅部分上傳");
case UPLOAD_ERR_NO_FILE: die("未選擇文件");
default: die("未知上傳錯誤代碼: $error");
}
}
// 調用示例
$f = $_FILES['userfile'];
uploadFile($f['type'], $f['name'], pathinfo($f['name'], PATHINFO_EXTENSION),
$f['size'], $f['error'], $f['tmp_name'], time() . ".jpg", "./uploads/");
echo "上傳成功!";
?>
| 風險點 | 防範措施 |
|---|---|
| MIME 偽造 | 不可信任 $_FILES['type'],需使用 finfo_file() 檢測真實 MIME:$finfo = finfo_open(FILEINFO_MIME_TYPE); $realMime = finfo_file($finfo, $tmp_name); |
| 擴展名繞過 | 白名單驗證擴展名;重命名文件 (時間戳+隨機數+白名單後綴);禁止解析上傳目錄腳本 (.php, .phtml 等)。 |
| 文件內容檢測 | 圖片類用 getimagesize() 驗證真實性;掃描文件頭魔數;集成 ClamAV 病毒掃描。 |
| 目錄遍歷 | 嚴格過濾文件名中的 ../、..\\;使用 basename() 獲取純文件名。 |
| 權限控制 | 上傳目錄設置 不可執行 (Nginx/Apache 禁用腳本解析);文件權限 0644,目錄 0755。 |
| DoS 攻擊 | 限制單文件/總大小;限制并發上傳數;使用獨立存儲服務 (OSS/S3) 卸載主服務器。 |
| 臨時文件殘留 | 異常分支確保 @unlink($tmp_name);定期清理 upload_tmp_dir。 |