章 42. 安全模式

PHP 的安全模式是為了試圖解決共享伺服器(shared-server)安全問題而設立的。在結構上,試圖在 PHP 層上解決這個問題是不合理的,但修改 web 伺服器層和動作系統層顯得非常不現實。因此許多人,特別是 ISP,目前使用安全模式。

保安措施和安全模式

表格 42-1. 保安措施和安全模式配置指令

名稱預設值可修改範圍更新記錄
safe_mode"0"PHP_INI_SYSTEM 
safe_mode_gid"0"PHP_INI_SYSTEM自 PHP 4.1.0 起可用
safe_mode_include_dirNULLPHP_INI_SYSTEM自 PHP 4.1.0 起可用
safe_mode_exec_dir""PHP_INI_SYSTEM 
safe_mode_allowed_env_vars"PHP_"PHP_INI_SYSTEM 
safe_mode_protected_env_vars"LD_LIBRARY_PATH"PHP_INI_SYSTEM 
open_basedirNULLPHP_INI_SYSTEM 
disable_functions""php.ini自 PHP 4.0.1 起可用
disable_classes""php.ini自 PHP 4.3.2 起可用
PHP_INI_* 常量的進一步詳細說明與定義見 ini_set()

以下是配置選項的簡要解釋。

safe_mode boolean

是否啟用 PHP 的安全模式。

safe_mode_gid boolean

預設情況下,安全模式在開啟檔案時會做 UID 比較檢查。若果想將其放寬到 GID 比較,則開啟 safe_mode_gid。是否在檔案訪問時使用 UIDFALSE)或是 GIDTRUE)來做檢查。

safe_mode_include_dir string

當從此目錄及其子目錄(目錄必須在 include_path 中或是用完整路徑來包括)包括檔案時越過 UID/GID 檢查。

從 PHP 4.2.0 開始,本指令可以接受和 include_path 指令類似的風格用冒號(Windows 中是分號)隔開的路徑,而不只是一個目錄。

特殊的限制實際上是一個前綴,而非一個目錄名。這也就是說「safe_mode_include_dir = /dir/incl」將容許訪問「/dir/include」和「/dir/incls」,若果它們存在的話。若果希望將訪問控制在一個特殊的目錄,那麼請在結尾加上一個斜線,例如:「safe_mode_include_dir = /dir/incl/」。

若果本指令的值為空,在 PHP 4.2.3 中以及 PHP 4.3.3 起具有不同 UID/GID 的檔案將不能被包括。在較早版本中,所有檔案都能被包括。

safe_mode_exec_dir string

若果 PHP 使用了安全模式,system() 和其它程式執行函數將拒絕啟動不在此目錄中的程式。必須使用 / 作為目錄分隔設定,內含 Windows 中。

safe_mode_allowed_env_vars string

設定某些環境變量可能是潛在的安全缺口。本指令包括有一個逗號分隔的前綴清單。在安全模式下,會員只能改變那些名字具有在這裡提供的前綴的環境變量。預設情況下,會員只能設定以 PHP_ 開頭的環境變量(例如 PHP_FOO = BAR)。

注: 若果本指令為空,PHP 將使會員可以修改任何環境變量!

safe_mode_protected_env_vars string

本指令包括有一個逗號分隔的環境變量的清單,最終會員不能用 putenv() 來改變這些環境變量。甚至在 safe_mode_allowed_env_vars 中設定了容許修改時也不能改變這些變量。

open_basedir string

將 PHP 所能開啟的檔案限制在特殊的目錄樹,內含檔案本身。本指令不受安全模式開啟或是關閉的影響。

當一個腳本試圖用例如 fopen() 或是 gzopen() 開啟一個檔案時,該檔案的位置將被檢查。當檔案在特殊的目錄樹之外時 PHP 將拒絕開啟它。所有的符號連線都會被解析,所以不可能通過符號連線來避開此限制。

特殊值 . 指明腳本的工作目錄將被作為基準目錄。但這有些危險,因為腳本的工作目錄可以輕易被 chdir() 而改變。

httpd.conf 檔案中中,open_basedir 可以像其它任何配置選項一樣用「php_admin_value open_basedir none」的方法關閉(例如某些虛擬主電腦中)。

在 Windows 中,用分號分隔目錄。在任何其它系統中用冒號分隔目錄。作為 Apache 模組時,父目錄中的 open_basedir 路逕自動被繼承。

用 open_basedir 特殊的限制實際上是前綴,不是目錄名。也就是說「open_basedir = /dir/incl」也會容許訪問「/dir/include」和「/dir/incls」,若果它們存在的話。若果要將訪問限制在僅為特殊的目錄,用斜線結束路徑名。例如:「open_basedir = /dir/incl/」。

注: 支援多個目錄是 3.0.7 加入的。

預設是容許開啟所有檔案。

disable_functions string

本指令容許你基於安全原因禁止某些函數。接受逗號分隔的函數名清單作為參數。 disable_functions 不受安全模式的影響。

本指令只能設定在 php.ini 中。例如不能將其設定在 httpd.conf

disable_classes string

本指令可以使你出於安全的理由禁用某些類。用逗號分隔類名。disable_classes 不受安全模式的影響。

本指令只能設定在 php.ini 中。例如不能將其設定在 httpd.conf

可用性說明: 本指令自 PHP 4.3.2 起可用。

參見 register_globalsdisplay_errorslog_errors

safe_mode 設定為 on,PHP 將通過檔案函數或其目錄檢查現用的腳本的擁有者是否和將被動作的檔案的擁有者相符合。例如:
-rw-rw-r--    1 rasmus   rasmus       33 Jul  1 19:20 script.php
-rw-r--r--    1 root     root       1116 May 26 18:01 /etc/passwd
運行 script.php
<?php
 readfile
('/etc/passwd');
?>
若果安全模式被啟動,則將會導致以下錯誤:
Warning: SAFE MODE Restriction in effect. The script whose uid is 500 is not
allowed to access /etc/passwd owned by uid 0 in /docroot/script.php on line 2

同時,或許會存在這樣的環境,在該環境下,寬鬆的 GID 檢查已經足夠,但嚴格的 UID 檢查反而是不適合的。可以用 safe_mode_gid 選項來控制這種檢查。若果設定為 On 則進行寬鬆的 GID 檢查;設定為 Off(預設值)則進行 UID 檢查。

除了 safe_mode 以外,若果設定了 open_basedir 選項,則所有的檔案動作將被限制在特殊的目錄下。例如:
<Directory /docroot>
  php_admin_value open_basedir /docroot
</Directory>
若果在設定了 open_basedir 選項後運行同樣的 script.php,則其結果會是:
Warning: open_basedir restriction in effect. File is in wrong directory in
/docroot/script.php on line 2

也可以單獨地屏蔽某些函數。請注意 disable_functions 選項不能在 php.ini 檔案外部使用,也就是說無法在 httpd.conf 檔案的按不同虛擬主電腦或不同目錄的模式來屏蔽函數。若果將如下內容加入到 php.ini 檔案:
disable_functions readfile,system
則會得到如下的輸出:
Warning: readfile() has been disabled for security reasons in
/docroot/script.php on line 2

警示

當然,這些 PHP 限制不適用於可執行檔案。