文件包含/讀取功能是 Web 應用高危區。本文剖析一個典型的不安全代碼,演示如何繞過 .. 替換與副檔名限制,並給出白名單防禦的最佳實踐。
<?php
$mod = isset($_GET['m']) ? trim($_GET['m']) : 'index';
$mod = str_replace("..", ".", $mod); // 嘗試過濾目錄遍歷
$file = "/home/www/blog/" . $mod . ".php";
@include($file);
?>
攻擊者傳入:?m=...%2F...%2F...%2F...%2Fetc%2Fpasswd%00
str_replace:.../.../ 經替換後變為 ../../(雙重編碼/多層替換繞過)。%00 (URL 編碼的 NULL) 在 C 語言層面會截斷字串,導致 .php 後綴被忽略,最終包含 /etc/passwd。str_replace 只替換一次,多層嵌套可繞過(如 ....// → ../)。javascript 替換可被 jjavascriptavascript 繞過)。$mod = isset($_GET['m']) ? trim($_GET['m']) : 'index';
$allowed = array('user', 'index', 'add', 'edit');
if (!in_array($mod, $allowed)) { exit('err!!!'); }
$mod = isset($_GET['m']) ? trim($_GET['m']) : 'index';
if (!preg_match('/^\w+$/', $mod)) { exit('err!!!'); }
僅允許字母、數字、底線,徹底杜絕目錄遍歷、空字節、路徑分隔符等危險字元。
basename() + 白名單,命令執行用 escapeshellarg()。exec, include 等)。%00 在檔案函數中不再生效)。open_basedir 限制 PHP 可存取的目錄範圍。安全防禦的核心在於「簡單、嚴格、可驗證」的白名單邏輯,而非複雜易繞過的黑名單替換。