本文展示一個單一檔案 upimage.php 即完成圖片上傳、資訊記錄、相簿顯示與表單呈現的完整相簿系統。程式碼緊湊但包含典型上傳流程所有關鍵步驟,適合作為 PHP 檔案上傳教學案例。
if ($upload))if ($picurl == "none") { echo "你沒有上傳任何文件."; exit; }
$v = opendir("image");
if ($v == 0) { mkdir("image"); $v = opendir("image"); }
$up = copy("$picurl", "image/$picurl_name"); // 關鍵:移動暫存檔
if ($up == 1) {
// 寫入 photo.txt 記錄
$fp = fopen("photo.txt", "a");
$mydate = date("Y年m月d日 h:i A");
$photonote = nl2br($photonote);
$text = "照片名:<a href='image/$picurl_name' target='_blank'>$picurl_name</a> <br>
照片大小:$picurl_size byte<br> 照片說明:<BR><span class='blue10'>$photonote</span><br>
<div align='right'>上傳人: $user($mydate)</div><hr>";
fwrite($fp, $text, strlen($text));
fclose($fp);
unlink($picurl); // 刪除暫存檔
closedir($v);
}
register_globals 與 PHP 4 上傳機制)$picurl:暫存檔完整路徑(如 /tmp/phpA1B2C3)$picurl_name:原始檔名$picurl_size:檔案大小(位元組)$picurl_type:MIME 型別(未使用)現代對應:$_FILES['picurl']['tmp_name']、['name']、['size']、['type']。
<?php readfile("photo.txt"); ?>
直接輸出純文字記錄檔(含 HTML 標籤),簡單但無分頁、無搜尋、無刪除功能。
<form action="upimage.php" method="post" enctype="multipart/form-data" name="UL">
<input type="file" name="picurl" size="15" accept="image/x-png,image/gif,image/jpeg">
<textarea name="photonote" cols="50" rows="5"></textarea>
<input type="text" name="user" size="10" maxlength="10">
<input type="Submit" name="upload" value="上傳">
</form>
enctype="multipart/form-data" 是檔案上傳的必要條件,accept 屬性提供客戶端檔案類型過濾(僅 UI 提示,伺服器仍需驗證)。
| 問題 | 風險/影響 | 修正建議 |
|---|---|---|
直接使用原始檔名 $picurl_name |
覆蓋同名檔、路徑遍歷(../../etc/passwd)、特殊字元導致路徑錯誤 |
重新命名:$newName = uniqid() . '_' . basename($name) 或 UUID + 副檔名 |
| 無檔案型別驗證 | 上傳惡意腳本(.php、.phtml)經 Web 伺服器解析導致 RCE |
1. 檢查 MIME $_FILES['picurl']['type'] 2. getimagesize() 驗證圖片 3. 儲存目錄設定 php_flag engine off / AddType text/plain .php |
| 無檔案大小限制 | 佔滿磁碟、DoS | php.ini: upload_max_filesize, post_max_size + 程式碼檢查 $_FILES['picurl']['size'] |
使用 copy() 而非 move_uploaded_file() |
無法確認檔案確實為 HTTP 上傳,理論上可被偽造 | 必須使用 move_uploaded_file($_FILES['picurl']['tmp_name'], $dest) |
記錄檔 photo.txt 直接輸出 |
XSS:說明欄位注入 <script>alert(1)</script> 將在顯示時執行 |
htmlspecialchars() 輸出轉義,或改用資料庫儲存結構化資料 |
| 目錄建立權限問題 | mkdir("image") 預設 0777 受 umask 影響,可能失敗或權限過大 |
mkdir("image", 0755, true) 遞迴建立並明確權限 |
併發寫入 photo.txt 無鎖定 |
多用戶同時上傳導致資料遺失/交錯 | 改用資料庫(自動行鎖)或 flock() 檔案鎖 |
photos 表(id, filename, original_name, size, mime, description, uploader, ip, created_at),支援分頁、搜尋、刪除、編輯。storage/app/public/photos/{year}/{month}/{uuid}.ext,資料庫只存相對路徑,配合 php artisan storage:link 或 Nginx X-Accel-Redirect 下載。此單檔案範例雖簡陋,但完整呈現了「接收 → 驗證 → 移動 → 記錄 → 顯示」的上傳核心流程。現代開發應將各環節模組化、服務化,並以安全性優先原則設計每一層防禦。