開啟PHP安全模式(請注意,PHP5.3將不再有安全模式)
開啟或者關閉php的安全模式是利用php.ini中的safe_mode選項:
代碼如下 |
複製代碼 |
safe_mode=On(使用安全模式) safe_mode=Off(關閉安全模式) 在apache的httpd.conf中VirtualHost的相應設定方法 php_admin_flag safe_mode On(使用安全模式) php_admin_flag safe_mode Off(關閉安全模式) 或者: php_admin_value safe_mode1(使用安全模式) php_admin_value safe_mode0(關閉安全模式)
|
安全模式配置指令:
名稱 |
預設值 |
可修改範圍 |
更新記錄 |
safe_mode |
"0" |
PHP_INI_SYSTEM |
|
safe_mode_gid |
"0" |
PHP_INI_SYSTEM |
自 PHP 4.1.0 起可用 |
safe_mode_include_dir |
NULL |
PHP_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_basedir |
NULL |
PHP_INI_SYSTEM |
|
disable_functions |
"" |
僅 php.ini |
自 PHP 4.0.1 起可用 |
disable_classes |
"" |
僅 php.ini |
自 PHP 4.3.2 起可用 |
當安全模式開啟的時候,以下函數列表的功能將會受到限制:
函數名 |
限制 |
dbmopen() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
dbase_open() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
filepro() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
filepro_rowcount() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
filepro_retrieve() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
ifx_* |
sql_safe_mode 限制, (!= safe mode) |
ingres_* |
sql_safe_mode 限制, (!= safe mode) |
mysql_* |
sql_safe_mode 限制, (!= safe mode) |
pg_loimport() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
posix_mkfifo() |
檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
putenv() |
遵循 ini 設定的 safe_mode_protected_env_vars 和 safe_mode_allowed_env_vars 選項。請參考 putenv() 函數的有關文檔。 |
move_uploaded_file() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
chdir() |
檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
dl() |
本函數在安全模式下被禁用。 |
backtick operator |
本函數在安全模式下被禁用。 |
shell_exec()(在功能上和 backticks 函數相同) |
本函數在安全模式下被禁用。 |
exec() |
只能在 safe_mode_exec_dir 設定的目錄下進行執行操作。基於某些原因,目前不能在可執行對象的路徑中使用 ..。escapeshellcmd() 將被作用於此函數的參數上。 |
system() |
只能在 safe_mode_exec_dir 設定的目錄下進行執行操作。基於某些原因,目前不能在可執行對象的路徑中使用 ..。escapeshellcmd() 將被作用於此函數的參數上。 |
passthru() |
只能在 safe_mode_exec_dir 設定的目錄下進行執行操作。基於某些原因,目前不能在可執行對象的路徑中使用 ..。escapeshellcmd() 將被作用於此函數的參數上。 |
popen() |
只能在 safe_mode_exec_dir 設定的目錄下進行執行操作。基於某些原因,目前不能在可執行對象的路徑中使用 ..。escapeshellcmd() 將被作用於此函數的參數上。 |
fopen() |
檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
mkdir() |
檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
rmdir() |
檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
rename() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
unlink() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
copy() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 (on source and target ) |
chgrp() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
chown() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
chmod() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 另外,不能設定 SUID、SGID 和 sticky bits |
touch() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 |
symlink() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 (注意:僅測試 target) |
link() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 (注意:僅測試 target) |
apache_request_headers() |
在安全模式下,以“authorization”(區分大小寫)開頭的標題將不會被返回。 |
header() |
在安全模式下,如果設定了 WWW-Authenticate,當前指令碼的 uid 將被添加到該標題的 realm 部分。 |
PHP_AUTH 變數 |
在安全模式下,變數 PHP_AUTH_USER 、PHP_AUTH_PW 和 PHP_AUTH_TYPE 在 $_SERVER 中不可用。但無論如何,您仍然可以使用 REMOTE_USER 來擷取使用者名稱稱(USER)。(注意:僅 PHP 4.3.0 以後有效) |
highlight_file(), show_source() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 (注意,僅在 4.2.1 版本後有效) |
parse_ini_file() |
檢查被操作的檔案或目錄是否與正在執行的指令碼有相同的 UID(所有者)。 檢查被操作的目錄是否與正在執行的指令碼有相同的 UID(所有者)。 (注意,僅在 4.2.1 版本後有效) |
set_time_limit() |
在安全模式下不起作用。 |
max_execution_time |
在安全模式下不起作用。 |
mail() |
在安全模式下,第五個參數被屏蔽。(注意,僅自 PHP 4.2.3 起受影響) |
同樣的,一些php擴充中的函數也將會受到影響。(載入模組:在安全模式下dl函數將被禁止,如果要載入擴充的話,只能修改php.ini中的擴充選項,在php啟動的時候載入)
在php安全模式開啟的時候,需要執行系統程式的時候,必須是在safe_mode_exec_dir選項指定目錄的程式,否則執行將失敗。即使允許執行,那麼也會自動的傳遞給escapeshellcmd函數進行過濾。
以下執行命令的函數列表將會受到影響:
exec,shell_exec,passthru,system,popen
另外,背部標記操作符(`)也將被關閉。
當運行在安全模式下,雖然不會引起錯誤,但是putenv函數將無效。同樣的,其他一些嘗試改變php環境變數的函數set_time_limit, set_include_path也將被忽略。
安全模式後的影響:
當函數在訪問檔案系統的時候將進行檔案所有者的檢查。預設情況下,會檢查該檔案所有者的使用者id,當你能夠修改檔案所有者的組id(gid)為safe_mode_gid選項所指定的。
如果你有一個共用庫檔案在你的系統上,當你碰到需要include或require的時候,那麼你可以使用safe_mode_include_dir選項來設定你的路徑,保證你的代碼正常工作。(包含路徑:如果你想要使用safe_mode_include_dir選項包含更多的包含路徑,那麼你可以象include_path選項一樣,在unix/linux系統下使用冒號進行分割,在windows下使用分號進行分割)
比如你想要在安全模式下包含/usr/local/include/php下的檔案,那麼你可以設定選項為:
safe_mode_include_dir=/usr/local/include/php
如果你的包含的檔案是需要執行的,那麼你可以設定safe_mode_exec_dir選項。
比如你需要/usr/local/php-bin路徑下的檔案是可以執行的,那麼可以設定選項為:
safe_mode_exec_dir=/usr/local/php-bin
(可執行:如果你執行的程式在/usr/bin目錄下,那麼你可以把這些的二進位檔案,串連到你指定選項下能夠執行的路徑)
如果你想設定某些環境變數,那麼可以使用safe_mode_allowed_env_vars選項。這個選項的值是一個環境變數的首碼,預設是允許php_開頭的環境變數,如果你想要改變,可以設定該選項的值,多個環境變數首碼之間使用逗號進行分割。
比如下面允許時區的環境變數tz,那麼修改該選項的值為:
safe_mode_allowed_env_vars=php_,tz
除了安全模式以外,php還提供了許多其他許多特徵來保證php的安全。
1、[隱藏php的版本號碼]
你能夠在php.ini裡使用expose_php選項來防止web伺服器泄露php的報告資訊。如下:
expose_php=on
利用整個設定,你能夠阻礙一些來自自動指令碼針對web伺服器的攻擊。通常情況下,http的頭資訊裡麵包含了如下資訊:
server:apache/1.3.33(unix)php/5.2.4mod_ssl/2.8.16openssl/0.9.7c
在expose_php選項開啟以後,php的版本資訊將不包含在上面的頭資訊裡。
當然,使用者訪問網站的時候同樣能夠看到.php的副檔名。如果你想整個的使用不同的副檔名,你需要在httpd.conf中找到如下這行:
addtype application/x-httpd.php
你就可以修改.php為任何你喜歡的副檔名。你能夠指定任意多個的副檔名,中間使用空格進行分割。如果你想在伺服器端使用php來解析.html和.htm檔案的時候,那麼你設定選項如下:
addtype application/x-httpd.html.htm
(解析html:配置你的web伺服器使用php去解析所有的html檔案,但是如果非伺服器端代碼也需要php去解析,會影響伺服器的效能。靜態頁面你可以使用不同的副檔名,這樣能夠消除對php指令碼引擎的依賴,增強效能。)
2、[檔案系統安全]
安全模式限制了指令碼所有者只能訪問屬於自己的檔案,但是你可以使用open_basedir選現來指定一個你必須訪問的目錄。如果你指定了一個目錄,php將拒絕訪問除了該目錄和該目錄子目錄的其他目錄。open_basedir選項能夠工作在安全模式之外。
限制檔案系統只能訪問/tmp目錄,那麼設定選項為:
open_basedir=/tmp
3、[函數存取控制]
你能夠在disable_functions選項中使用逗號分割來設定函數名,那麼這些函數將在php指令碼中被關閉。這個設定能夠工作在安全模式之外。
disable_functions=dl
當然,同樣的你能夠使用disable_classes選項來關閉對一些類的訪問。
4、[資料庫安全]
假設你的php指令碼中包含一個基於表單值來執行的mysql查詢:
$sql=”update mytable set col1=”.$_post["value"].”where col2=’somevalue’”;
$res=mysql_query($sql,$db);
你希望$_post["value"]包含一個整數值來更新你的列col1。可是,一個惡意使用者能夠輸入一個分號在表單欄位裡,接著,是一段他/她想被任意執行的sql語句。
舉例,假設下面是$_post["value"]提交的值:
0;insert into admin_users(username,password) values (‘me’,'mypassword’);
那麼當這個查詢發送給mysql查詢的時候,那麼就變成了下面這條sql:
update mytable set col1=0;
insert into admin_users(username,password) values (‘me’,'mypassword’);
where col2=’somevalue’;
這明顯是一個有害的查詢!首先這個查詢會在mytable表裡更新col1。這個並沒有什麼麻煩的,但是第二個運算式,它將執行insert運算式來插入一個能登陸的新管理員。第三個運算式就廢棄了,但同時sql解析器將拋出一個錯誤,這個有害的查詢才完成。這個攻擊就是大家常說的sql injection(註:sql注入)。
當然,sql injection存在一個問題,對方必須瞭解你的資料庫結構。在這個例子中,攻擊者是知道你有一個表admin_users,並且知道包含username和password欄位,同時,儲存的密碼是沒有加密的。
除了你自己,一般的網站訪問者是不知道這些關於資料庫的資訊。可是,如果你使用了一個開發原始碼的線上電子商務程式,或者使用一個自由的討論版程式,這些資料表的定義都是已知的,或者有一些使用者能夠訪問到你的資料庫。
此外,你的指令碼輸出會提示一個查詢錯誤,這些資訊裡包含了很多關於資料庫結構的重要訊息。在一個正常工作的網站上,你應該考慮設定display_errors選項為off,並且使用log_errors來代替display_errors,把警告和錯誤資訊插入到檔案中。
(資料庫許可權:它是一個非常重要的東西,你只有正確的許可權,才能通過指令碼正確的串連資料庫。你應該不要在指令碼中使用管理員去串連資料庫。如果你這麼做,那麼一個攻擊者將可能擷取全部的資料庫許可權,並且包括其他相同伺服器的許可權。攻擊者將可能運行grant或create user命令來擷取更多的存取權限。)
如果你要防止sql injection攻擊,你必須保證使用者表單裡提交的內容不是一個能夠執行的sql運算式。
前一個例子中,我們使用一個整型值來進行更新。如果在單引號後面跟上一個字串,這個攻擊者在分號之前必須提交一個閉合的引用在整個sql運算式中。可是,當magic_quotes_gpc選項是開啟的時候,在web表單中提交的引號將自動被轉義。
為了防止被惡意的攻擊者進行sql injection攻擊,你應該總是確認提交的資料是合法的。如果你需要的是一個整數值,那麼你可以使用is_numeric函數來測試這個表達值,或者使用settype函數來轉換為一個數字,清除任何一個傻傻的sql語句。
如果你開發的程式需要幾個提交的值在一個sql運算式裡,你能夠使用sprintf函數來構建一個sql字串,使用格式化字元來指示資料類型的每個值。看下面的例子:
$sql=sprintf(“update mytable set col1=%d where col2=’%s’”, $_post["number"], mysql_escape_string($_post["string"]));
在上一個例子中,整個mysql的資料已經被使用,所以這個字串已經通過mysql_escape_string函數進行過濾。對於其他資料庫,你可以使用addslashes函數進行轉義,或者使用其他方法