首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

is_uploaded_file() 安全機制深度解析:防止偽造檔案上傳攻擊

     編輯:PHP 相關知識

is_uploaded_file() 是 PHP 內建的安全函數,用於驗證檔案是否真正是透過 HTTP POST 上傳的。許多開發者僅知其用法,卻不解其背後防禦的具體攻擊向量。本文深入剖析 /etc/passwd 範例的含義、偽造上傳攻擊原理,並對比兩種上傳代碼的安全性差異。

核心問題:什麼是 /etc/passwd?

/etc/passwd 是 Linux/Unix 系統儲存用戶帳號資訊的關鍵系統檔案(含用戶名、UID、GID、家目錄、Shell 等)。雖然現代系統密碼雜湊存放於 /etc/shadow,但 /etc/passwd 仍屬敏感資訊,不可公開讀取。

攻擊場景:偽造上傳路徑遍歷

假設上傳處理代碼未使用 is_uploaded_file(),直接信任 $_FILES['file']['tmp_name']:

<?php
// 危險代碼:未驗證來源
$dest = "./uploads/" . $_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $dest);

惡意用戶可構造請求(透過代理工具、curl、瀏覽器擴充套件修改表單資料):

  • $_FILES['file']['tmp_name'] = /etc/passwd(或 ../../etc/passwd)
  • $_FILES['file']['name'] = passwd.txt

若 PHP 進程權限足夠(如以 root 執行、或開啟 open_basedir 限制不當),move_uploaded_file() 可能將系統敏感檔案「移動」到 Web 目錄下,導致洩露。甚至若配合 copy()、file_get_contents() 等函數,可直接讀取任意可讀檔案。

is_uploaded_file() 如何防禦?

PHP 內部維護一份本次請求真正透過 HTTP multipart/form-data 上傳的暫存檔路徑清單。is_uploaded_file($path) 僅檢查 $path 是否在該清單中:

  • True:確為本次上傳的暫存檔,安全可用。
  • False:路徑非上傳暫存檔(可能是偽造的系統路徑、舊暫存檔、或不存在),拒絕處理。

因此,任何處理上傳檔案的代碼,必須先通過 is_uploaded_file() 驗證。

兩段代碼安全性對比

代碼 A:直接使用 copy() + 基於原始檔名

$classnotes = "./" . $_FILES['classnotes']['name'];
copy($_FILES['classnotes']['tmp_name'], "./classnotes/" . basename($classnotes));

風險:

  • 未驗證 tmp_name 來源,易受偽造路徑攻擊。
  • 直接使用用戶提供的 name,可能包含路徑遍歷序列(../../../etc/passwd),導致任意寫入。
  • copy() 不會檢查來源是否為上傳檔案,且不會自動清理暫存檔。

代碼 B:使用 is_uploaded_file() + move_uploaded_file()

if (is_uploaded_file($_FILES['classnotes']['tmp_name'])) {
    $safe_name = basename($_FILES['classnotes']['name']); // 僅取檔名部分
    $dest = "./classnotes/" . $safe_name;
    // 進一步建議:重命名為隨機名稱、驗證副檔名/MIME、限制大小
    move_uploaded_file($_FILES['classnotes']['tmp_name'], $dest);
} else {
    echo "上傳失敗:非法檔案來源";
}

優勢:

  • is_uploaded_file() 確保來源合法。
  • move_uploaded_file() 同樣內建來源驗證,且處理完成後自動刪除暫存檔(節省磁碟空間、避免殘留)。
  • basename() 移除目錄成分,阻斷路徑遍歷(但仍建議自行生成隨機檔名)。

企業級上傳安全清單

  1. 驗證來源:is_uploaded_file() / move_uploaded_file()。
  2. 重命名檔案:使用 uniqid()、hash()、UUID 等生成隨機檔名,儲存原始檔名於資料庫。
  3. 驗證副檔名與 MIME:白名單機制(如 ['jpg','png','gif','pdf']),結合 finfo_file() 檢測真實 MIME。
  4. 限制檔案大小:php.ini upload_max_filesize、post_max_size + 代碼端二次檢查。
  5. 儲存路徑非 Web 可執行:上傳目錄設定 php_flag engine off / Nginx location ~ \.php$ { deny all; }。
  6. 掃描惡意內容:整合 ClamAV、VirusTotal API 等殺毒引擎。
  7. 設定 open_basedir:限制 PHP 可存取的目錄範圍。

總結

/etc/passwd 範例揭示的核心風險是「信任用戶端提供的檔案路徑」。is_uploaded_file() 是 PHP 提供的第一道防線,配合 move_uploaded_file()、隨機重命名、MIME 驗證、目錄隔離等纵深防禦措施,才能構建安全可靠的檔案上傳功能。切記:永遠不要信任 $_FILES 中的任何客戶端可控欄位。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved