章 26. 檔案系統安全

PHP 遵從大多數伺服器系統中關於檔案和目錄權限的安全機制。這就使管理員可以控制哪些檔案在檔案系統內是可讀的。必須特別注意的是全局的可讀檔案,並確保每一個有權限的會員對這些檔案的讀取動作都是安全的。

PHP 被設計為以會員層級來訪問檔案系統,所以完全有可能通過編寫一段 PHP 代碼來讀取系統檔案如 /etc/passwd,變更網路連線以及傳送大量列印任務等等。因此必須確保 PHP 代碼讀取和寫入的是合適的檔案。

請看下面的代碼,會員想要移除自己主目錄中的一個檔案。假設此情形是通過 web 介面來管理檔案系統,因此 Apache 會員有權移除會員目錄下的檔案。

例子 26-1. 不對變量進行安全檢查會導致……

<?php
// 從會員目錄中移除特殊的檔案
$username $_POST['user_submitted_name'];
$homedir "/home/$username";
$file_to_delete "$userfile";
unlink ("$homedir/$userfile");
echo 
"$file_to_delete has been deleted!";
?>
既然 username 變量可以通過會員表單來送出,那就可以送出別人的會員名和檔案名,並移除該檔案。這種情況下,就要考慮其它模式的認證。想想看若果送出的變量是「../etc/」和「passwd」會發生什麼。上面的代碼就等同於:

例子 26-2. ……檔案系統攻擊

<?php
// 移除硬碟中任何 PHP 有訪問權限的檔案。若果 PHP 有 root 權限:
$username "../etc/";
$homedir "/home/../etc/";
$file_to_delete "passwd";
unlink ("/home/../etc/passwd");
echo 
"/home/../etc/passwd has been deleted!";
?>
有兩個重要措施來防止此類問題。

  • 只給 PHP 的 web 會員很有限的權限。

  • 檢查所有送出上來的變量。

下面是改進的腳本:

例子 26-3. 更安全的檔案名檢查

<?php
// 移除硬碟中 PHP 有權訪問的檔案
$username $_SERVER['REMOTE_USER']; // 使用認證機制

$homedir "/home/$username";

$file_to_delete basename("$userfile"); // 去除變量中的路徑
unlink ($homedir/$file_to_delete);

$fp fopen("/home/logging/filedelete.log","+a"); // 記錄移除動作
$logstring "$username $homedir $file_to_delete";
fwrite ($fp$logstring);
fclose($fp);

echo 
"$file_to_delete has been deleted!";
?>
然而,這樣做仍然是有缺陷的。若果認證系統容許會員建立自己的登入會員名,而會員選取用「../etc/」作為會員名,系統又一次淪陷了。所以,需要加強檢查:

例子 26-4. 更安全的檔案名檢查

<?php
$username 
$_SERVER['REMOTE_USER']; // 使用認證機制
$homedir "/home/$username";

if (!
ereg('^[^./][^/]*$'$userfile))
     die(
'bad filename'); // 停止執行代碼

if (!ereg('^[^./][^/]*$'$username))
     die(
'bad username'); // 停止執行代碼
//後略……
?>

根據動作系統的不同,存在著各種各樣需要注意的檔案,內含聯繫到系統的裝置(/dev/ 或是 COM1)、配置檔(/ect/ 檔案和 .ini檔案)、常用的存儲區功能變數(/home/ 或是 My Documents)等等。由於此原因,建立一個策略禁止所有權限而只開放明確容許的通常更容易些。