檔案上傳驗證需「前端攔截、後端把關」雙重防護。前端提升使用者體驗,後端確保資料完整與安全。本文深入剖析判斷邏輯與常見誤區。
<form action="" method="post" enctype='multipart/form-data'>
檔案上傳:<input type="file" name="file" id="file"/>
<input type="submit" id="send" value="提交"/>
</form>
<script>
document.getElementById("send").onclick = function() {
var file = document.getElementById("file").value;
if (file.length < 1) {
alert('請選擇圖片');
return false;
}
};
</script>
缺點:使用者輸入任意文字也能通過,無法驗證真實檔案。
function CheckWorkFile() {
var obj = document.getElementById('fuMain');
if (obj.value == '') {
alert('請選擇要上傳的檔案');
return false;
}
// 取得副檔名(最後一個 . 之後)
var ext = obj.value.match(/^(.*)(\\.)(.{1,8})$/)[3].toLowerCase();
if (ext != 'doc') {
alert('檔案類型不正確,請選擇 .doc 檔案');
return false;
}
return true;
}
<?php
// 僅檢查 tmp_name 是否為空 — 不夠嚴謹
if (!empty($_FILES['file']['tmp_name'])) {
echo '已選擇檔案';
} else {
echo '請選擇檔案';
}
?>
問題:即使上傳失敗(錯誤代碼非 0),tmp_name 可能仍有值。
<?php
function isFileUploaded($fileInputName) {
if (!isset($_FILES[$fileInputName])) return false;
$file = $_FILES[$fileInputName];
// 單檔上傳
if (is_string($file['name'])) {
return $file['error'] === UPLOAD_ERR_OK
&& is_uploaded_file($file['tmp_name'])
&& $file['size'] > 0;
}
// 多檔上傳(陣列)
foreach ($file['name'] as $idx => $name) {
if ($file['error'][$idx] === UPLOAD_ERR_OK
&& is_uploaded_file($file['tmp_name'][$idx])
&& $file['size'][$idx] > 0) {
return true; // 至少有一個成功
}
}
return false;
}
// 使用範例
if (isFileUploaded('file')) {
echo '已選擇有效檔案';
} else {
echo '請選擇檔案或上傳失敗';
}
?>
僅檢查副檔名或 $_FILES['type'] 極不安全,需讀取檔案頭部 Magic Bytes:
<?php
function getRealFileType($filename) {
$file = fopen($filename, "rb");
$bin = fread($file, 2); // 僅讀 2 位元組
fclose($file);
$info = @unpack("C2chars", $bin);
$typeCode = intval($info['chars1'] . $info['chars2']);
$map = [
7790 => 'exe', 8297 => 'rar', 8075 => 'zip',
255216 => 'jpg', 7173 => 'gif', 6677 => 'bmp', 13780 => 'png'
];
// 修正負數情況(PHP unpack 有號數問題)
if ($info['chars1'] == -1 && $info['chars2'] == -40) return 'jpg';
if ($info['chars1'] == -119 && $info['chars2'] == 80) return 'png';
return $map[$typeCode] ?? 'unknown: ' . $typeCode;
}
// 使用範例
$allowed = ['jpg', 'png', 'gif'];
$realType = getRealFileType($_FILES['file']['tmp_name']);
if (!in_array($realType, $allowed)) {
die('禁止的檔案類型: ' . $realType);
}
?>
accept 屬性限制選擇框 + JS 檢查副檔名 + 檔案大小預估UPLOAD_ERR_OK → is_uploaded_file() → 檔案大小 → Magic Bytes 類型 → 檔名重命名 → 移動至安全目錄「信任前端,驗證後端」是檔案上傳安全的鐵律。Magic Bytes 檢測雖非絕對(可偽造頭部),但能擋絕大多數偽裝檔案,配合副檔名白名單與儲存目錄執行權限控制,構建多層防護。