一、簡(jiǎn)介
PHP 受大多數(shù)服務(wù)器系統(tǒng)中文件和目錄權(quán)限的內(nèi)置安全機(jī)制的影響。這允許控制文件系統(tǒng)中哪些文件是可讀的。應(yīng)該小心對(duì)待任何全局可讀的文件,要確保所有有權(quán)限訪問(wèn)該文件系統(tǒng)的用戶(hù)都可以安全地讀取文件。
PHP 被設(shè)計(jì)為以用戶(hù)級(jí)別訪問(wèn)文件系統(tǒng),因此完全可以編寫(xiě) PHP 腳本來(lái)讀取系統(tǒng)文件,例如 /etc/passwd,修改網(wǎng)絡(luò)連接,發(fā)送大量打印任務(wù)等。這有一些明顯的影響,因此需要確保讀寫(xiě)的是合適的文件。
二、示例腳本
請(qǐng)看下面的腳本,用戶(hù)表示想要?jiǎng)h除自己主目錄下的一個(gè)文件。 假設(shè) PHP web 界面通常用于文件管理, 因此 Apache 用戶(hù)允許刪除用戶(hù)主目錄中的文件。
不對(duì)變量檢查會(huì)導(dǎo)致….:
<?php // 從用戶(hù)主目錄移除一個(gè)文件 $username = $_POST['user_submitted_name']; $userfile = $_POST['user_submitted_filename']; $homedir = "/home/$username"; unlink("$homedir/$userfile"); echo "The file has been deleted!"; ?>
由于 username 和 filename 由用戶(hù)表單中提交,那就能提交屬于其他人的 username 和 filename,甚至可以刪除不被允許的文件。這種情況下, 應(yīng)該使用一些其它形式的身份驗(yàn)證。不妨考慮一下,如果提交的變量是 “../etc/” 和 “passwd” 會(huì)發(fā)生什么。上面代碼將等同于:
… 文件系統(tǒng)攻擊:
<?php // 刪除磁盤(pán)中任何 PHP 有訪問(wèn)權(quán)限的文件。如果 PHP 有 root 權(quán)限: $username = $_POST['user_submitted_name']; // "../etc" $userfile = $_POST['user_submitted_filename']; // "passwd" $homedir = "/home/$username"; // "/home/../etc" unlink("$homedir/$userfile"); // "/home/../etc/passwd" echo "The file has been deleted!"; ?>
有兩個(gè)重要措施來(lái)防止此類(lèi)問(wèn)題。
- PHP web 用戶(hù)二進(jìn)制文件僅允許有限的權(quán)限。
- 檢查所有提交上來(lái)的變量。
這是改進(jìn)的腳本:
更安全的文件名檢查:
<?php // 刪除磁盤(pán)中 PHP 有權(quán)訪問(wèn)的文件。 $username = $_SERVER['REMOTE_USER']; // 使用認(rèn)證機(jī)制 $userfile = basename($_POST['user_submitted_filename']); $homedir = "/home/$username"; $filepath = "$homedir/$userfile"; if (file_exists($filepath) && unlink($filepath)) { $logstring = "Deleted $filepath\n"; } else { $logstring = "Failed to delete $filepath\n"; } $fp = fopen("/home/logging/filedelete.log", "a"); fwrite($fp, $logstring); fclose($fp); echo htmlentities($logstring, ENT_QUOTES); ?>
然而,這樣做也是有缺陷的。如果認(rèn)證系統(tǒng)允許用戶(hù)創(chuàng)建自己的登錄用戶(hù)名, 而用戶(hù)選擇 “../etc/” 作為登錄名,系統(tǒng)將再次暴露。 出于這個(gè)原因,需要編寫(xiě)自定義檢查:
更安全的文件名檢查:
<?php $username = $_SERVER['REMOTE_USER']; // 使用認(rèn)證機(jī)制 $userfile = $_POST['user_submitted_filename']; $homedir = "/home/$username"; $filepath = "$homedir/$userfile"; if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) { die("Bad username/filename"); } //等等... ?>
根據(jù)操作系統(tǒng)的不同,需要關(guān)心各種各樣的文件,比如設(shè)備條目(/dev/ 或 COM1)、配置文件(/etc/ 文件和 .ini 文件)、 眾所周知的文件存儲(chǔ)區(qū)域(/home/, My Documents)等等。出于這個(gè)原因,創(chuàng)建一個(gè)禁止所有權(quán)限而只開(kāi)放明確允許的策略通常更容易些。