Linux下Apache與PHP安全相關設置

列印

  以下文章是對Apache的安全相關設置,其實不光是LINUX,如果主機是WINDOWS 也是要多注意.

 此文章來源自:http://xok.la/2009/02/linux_apache_php_set.html

 --------------------------------我是分隔線-------------------------------------------

對於提供公共網路服務的ISP,基於安全性理由,建議apache和php都使用最小權限的公用設置.
針對特定用戶所提出的涉及安全性能的特殊要求, 可以在不改變全局性的共用設置的情況下,
通過利用Apache Virtualhost的PHP擴展功能來實現. 只需在相應用戶的Virtualhost的設置
段落中插入php_value,php_admin_value或php_admin_flag指令,就可以使該用戶具有與全局
設置不同的權限和行為.

針對Apache的特定虛擬用戶進行單獨配置的相關語法如下:

php_admin_value name 1|0|string                (value控制具體的參數)
php_admin_flag name on|off                (flag控制on或off,適用於Apache2.20版本)

請注意: 上述針對虛擬用戶的設置命令,可以直接設置在單元裡面,或者設置在
相應用戶的單元裡面.

例如,要將ernest這個用戶的register_globals功能打開,並且將upload_max_filesize調高到
5M,同時關閉safe_mode,但又不影響其他用戶,就可以在該用戶的VirtualHost裡面加如下幾行:


        php_value upload_max_filesize 5M
        php_value register_globals 1

        php_value safe_mode 0
        php_flag safe_mode Off

        php_admin_value safe_mode 0        #(for Apache2.20)
        php_admin_flag safe_mode Off         #(for Apache2.20)

在php.ini配置文件中的大部分功能,均可以用這種方式來調整,調整後應重新啟動apache,然後
就可以在phpinfo中看到中間欄的Local Value同右邊欄位的Master Value是不同值.

請注意: 有些參數值的設定方法跟它們在php.ini配置文件中的設置方法可能不一樣,例如
上面的”register_globals 1″, 原來在php.ini中應是”register_globals On”.
################################################################################

================================================================================
(1) safe_mode: 以安全模式運行PHP;
——————————————————————————–
在php.ini文件中使用如下選項(這是影響全局的設置):

safe_mode = On                (使用安全模式)
safe_mode = Off                (關閉安全模式)

PHP的安全模式是為了試圖解決共享伺服器(shared-server)的安全問題而專門設立的. 然而
從結構上看, 試圖在PHP層面上解決這個問題其實是不合理的, 只是考慮到修改WEB伺服器層
和作業系統層都顯得非常的不現實, 因此許多使用者,特別是提供公共網路服務的ISP供應商,
大多都在其伺服器中要求以安全模式來運行PHP,用以防止合法用戶的跨站讀取或越權操作等
危險行為, 以及將非授權用戶的惡意行為所造成的影響降到最低範圍.

參考網址: http://hk2.php.net/features.safe-mode

在Apache的httpd.conf中VirtualHost的相應設置方法(這是針對特定用戶的設置):
php_admin_flag safe_mode On        (使用安全模式)
php_admin_flag safe_mode Off        (關閉安全模式)
或者:
php_admin_value safe_mode 1        (使用安全模式)
php_admin_value safe_mode 0        (關閉安全模式)

嚴重警告: 如果在全局性設置中已經啟用了safe_mode的功能,但又在特別的用戶虛擬空間中
關閉該用戶的safe_mode的功能,這就等於給予了該用戶特殊的權限,允許他不須受safe_mode
的限制而自由地使用系統的服務,也就是說所有原來被全局性的safe_mode功能所禁止的行為,
例如跨站讀取或越權操作等都可以被該用戶執行, 這就好像在原本安全設防的金庫中打開了
一個可供該用戶自由進出的洞,因此任何使用該用戶空間的應用都不再被全局性的safe_mode
保護所限制,當然這就意味著整個系統的安全性都可能會受到該用戶空間的影響,包括可能因
該空間應用的漏洞而導致整個系統被入侵等等. 所以,這樣的針對特定用戶的特殊設置,應當
作為一種特許授權的方式來考慮, 並有必要建立有效的監控機制以防止該用戶濫用系統資源,
否則, 一旦該用戶變得不再可信任或他的網站程式存在漏洞, 那麼您的整體系統所受的影響
就會同完全沒有啟用safe_mode一模一樣.
================================================================================

================================================================================
(2) safe_mode_include_dir: 無需UID/GID檢查的目錄
——————————————————————————–
當您按照前面(1)所述之設置啟用PHP的安全模式之後,PHP的腳本在運行時就會對所有被操作
的目錄以及文件進行針對UID/GID的匹配性檢查: 即檢查被操作目錄或文件的UID或GID,是否
同當前PHP腳本文件的UID或GID一樣.

然而, 如果您的系統允許用戶的PHP腳本訪問公共路徑的話(例如很多較舊的Forum或Gallery
程式都會直接引用系統文件來擴展當時PHP還未能支持的功能), 那麼這種設置就會造成麻煩.

而使用safe_mode_include_dir設置可以指定某些目錄, 當PHP腳本操作這些目錄及其子目錄
時(該目錄必須在include_path中或者用完整路徑來包含), 則允許越過UID/GID檢查,即不對
該目錄進行UID/GID匹配性檢查.

從PHP4.2.0開始, 這個指令已經可以接受同include_path指令類似的風格, 即用分號隔開的
多個路徑, 而以前則只能指定單一個目錄. 同open_basedir一樣, 它所指定的路徑實際上也
是一個字符串的前綴限制,而非針對該目錄名稱空間的操作.

例如如果指定: “safe_mode_include_dir = /dir/incl”, 那麼所有的PHP腳本都將允許任意
訪問 “/dir/include”和“/dir/incls” 路徑(如果它們存在的話). 因此, 如果您希望將訪問
控制在一個指定的目錄裡面, 就必須在上述設置的指定路徑的結尾加上一個斜線, 例如:
“safe_mode_include_dir = /dir/incl/”

請注意: VirtualHost會自動繼承php.ini中的safe_mode_include_dir設置.
================================================================================

================================================================================
(3) open_basedir: 將用戶可操作的文件限制在某目錄下;
——————————————————————————–
如下是php.ini中的原文說明以及默認配置:
; open_basedir, if set, limits all file operations to the defined directory
; and below. This directive makes most sense if used in a per-directory or
; per-virtualhost web server configuration file. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
open_basedir = .

open_basedir可將用戶訪問文件的活動範圍限制在指定的區域,通常是其家目錄的路徑,也
可用符號”.”來代表當前目錄。注意用open_basedir指定的限制實際上是前綴,而不是目錄名。
舉例來說: 若”open_basedir = /dir/user”, 那麼目錄 “/dir/user” 和 “/dir/user1″都是
可以訪問的。所以如果要將訪問限制在僅為指定的目錄,請用斜線結束路徑名。例如設置成:
“open_basedir = /dir/user/”

open_basedir也可以同時設置多個目錄, 在Windows中用分號分隔目錄,在任何其它系統中用
冒號分隔目錄。當其作用於Apache模塊時,父目錄中的open_basedir路徑自動被繼承。

有三種方法可以在Apache中為指定的用戶做獨立的設置:

(a) 在Apache的httpd.conf中Directory的相應設置方法:


     php_admin_value open_basedir /usr/local/apache/htdocs/
#設置多個目錄可以參考如下:
     php_admin_value open_basedir /usr/local/apache/htdocs/:/tmp/

(b) 在Apache的httpd.conf中VirtualHost的相應設置方法:

php_admin_value open_basedir /usr/local/apache/htdocs/
#設置多個目錄可以參考如下:
php_admin_value open_basedir /var/www/html/:/var/tmp/

(c) 因為VirtualHost中設置了open_basedir之後, 這個虛擬用戶就不會再自動繼承php.ini
中的open_basedir設置值了,這就難以達到靈活的配置措施, 所以建議您不要在VirtualHost
中設置此項限制. 例如,可以在php.ini中設置open_basedir = .:/tmp/, 這個設置表示允許
訪問當前目錄(即PHP腳本文件所在之目錄)和/tmp/目錄.

請注意: 若在php.ini所設置的上傳文件臨時目錄為/tmp/, 那麼設置open_basedir時就必須
包含/tmp/,否則會導致上傳失敗. 新版php則會提示”open_basedir restriction in effect”
警告資訊, 但move_uploaded_file()函數仍然可以成功取出/tmp/目錄下的上傳文件,不知道
這是漏洞還是新功能.
================================================================================

================================================================================
(4) disable_functions: 單獨地屏蔽某些函式(常用於禁止普通用戶執行系統函數);
——————————————————————————–
這個指令允許你基於安全原因直接禁止某些確定的函式(通常是攸關系統安全的函數),例如:
disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var

disable_functions接受逗號分隔的函式名列表作為參數, 它不受安全模式的影響,而且只能
設置在php.ini中用作全局性配置, 不能將其設置在httpd.conf中針對單獨用戶來進行設置.

從php-4.0.1開始在php.ini裡引入了此項功能, 這個功能非常有用, 可以用它禁止用戶使用
一些具有潛在的危險性的函數, 例如: passthru,exec,system,popen 等等. 當您在php.ini
中加上 disable_functions = passthru,exec,system,popen 配置後, PHP在執行這些函數
時就只會顯示錯誤提示: Warning: system() has been disabled for security reasons

下面舉個例子來看看這個安全性設置的重要程度:

我們知道PHP腳本可以採用很多perl的特性,例如通過一種叫shell_exec的方法來執行系統的
命令, 只需在一對反引號(“)中包含調用系統命令的script代碼, 就能執行相應的系統命令.
例如:

  $output = `ls /etc -al`;
  echo $output;
?>

顯然,如果您的系統不加限制的話,那麼任何用戶都可以通過諸如 `cat /etc/passwd` 這樣的
命令攫取系統資訊或進行破壞行為. 這對於提供公共服務的ISP供應商來說, 等於是打開自家
金庫大門讓所有客戶自由出入, 而且一旦某些客戶的PHP程式存在安全漏洞(就目前PHP程式員
的平均水準來看,存在嚴重的漏洞基本上是不可避免的)的話, 那麼只需最低級的駭客都可以
很簡單地完全操控您的主機了. 因此一定要防止Linux用戶在PHP程式中通過“來執行script
腳本,這可以通過在PHP.INI中設置: disable_functions = shell_exec,system,exec 來禁止
PHP調用相關系統函數.

典型的安全性配置,請參考如下設置:

disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var

若允許用戶調試程式,則可配置如下:

disable_functions = shell_exec,system,exec,passthru

PHP中一些常用但有安全風險的函數:
unlink,mkdir,touch,fgets,popen,proc_open,link,symlink,phpinfo
建議: 應兼顧到商業服務的完整性和安全性,請酌情考慮是否禁止使用它們.

請注意: disable_functions選項不能在php.ini文件外部使用,也就是說您無法在httpd.conf
文件中按不同虛擬主機或不同目錄的方式來屏蔽或者開啟函式。
================================================================================

================================================================================
5) register_globals: 禁止註冊全局變量;
——————————————————————————–

register_globals = On        (自動註冊為全局變量)
register_globals = Off        (不可註冊為全局變量)

一般情況下,用戶都是在HTML網頁裡通過HTTP協議,來提交GET,POST和COOKIE數據(簡稱為GPC)
的. 而PHP程式如何獲得用戶提交的這些變量數據,則還需依賴於php.ini配置中一個有爭議的
設置,即是register_globals參數來決定.

顧名思義,register_globals的意思就是註冊為全局變量, 所以當設置為On的時候, 通過頁面
傳遞過來的值就會被直接的註冊為全局變量,可以很方便地提供給PHP程式直接使用;而當設置
為Off的時候,PHP程式要使用網頁傳遞過來的變數,就需要到特定的陣列裡才能得到它.

在PHP4.3.0以後,register_globals默認情況下被設置為Off; 但是幾年前,register_globals
的默認值還是打開的,所以現在依然還存在很多需要啟用它的程式代碼.

請注意: 當設置為 register_globals = Off 之後,不僅會影響到PHP如何獲取從

的URL
所傳遞過來的數據,也會影響到PHP獲取session和cookie的方式. 當關閉register_globals時,
PHP程式就必須使用相應的陣列來獲取session和cookie(例如$_SESSION[]和$_COOKIE). 同時
對於session的處理也有一些改變,比如使用session_register()就顯得再也沒有必要了,而且
會失效(關於具體的變化請查看PHP手冊頁的Session handling functions文檔之描述).

啟用register_globals本身並無安全風險,但是它為跟蹤用戶輸入和確保應用程式安全增加了
難度. 因為一旦打開了register_globals,那麼在全局名稱空間和$_GET,$_POST或$_COOKIE的
陣列中,將會自動創建GET,POST和COOKIE傳遞到PHP腳本的所有變量. 如果您的PHP程式利用了
這些變量來作安全標識(例如很多程式員喜歡用COOKIE值來辨別用戶身份),那麼任何人都可以
用通過URL所傳遞的數值來獲取並假冒用戶身份,顯然這就不再具備安全性了.

另外,一些粗枝大葉或沒有責任心的程式員所寫的PHP代碼, 例如: ,
也很可能讓駭客或惡意使用者有機可乘,使他們得以利用register_globals的弱點來進行諸如
代碼注入或內存溢位等方式的攻擊,從而很輕易地造成系統性的災難.

重要提示: 自PHP4.2.0起,PHP中的選項register_globals的默認值被設為off了,PHP社區鼓勵
大家不要依賴於這個選項,而用其它方法來替代,例如superglobals。

在Apache的httpd.conf中VirtualHost的相應設置方法:

php_admin_flag register_globals on
或者:
php_admin_value register_globals 1

================================================================================

================================================================================
6) magic_quotes_gpc: 令敏感字元轉義
——————————————————————————–

magic_quotes_gpc = On
magic_quotes_gpc = Off

magic_quotes_gpc選項是php中的一個重要的安全設置, 當該選項為ON, 也就是打開的時候,
所有從GET,POST,COOKIE傳遞過來的數據之中的特殊字元(如’”\等),以及NULL等元字符都會
被自動的加上\以實現轉義,這個選項使得SQL注入或者插入代碼,以及XSS中引入字符串或者
改變程式流程變得更加困難。

在php.ini配置文件中是默認啟用magic_quotes_gpc設置(即為On)的,這相當於自動對所提交
的GET,POST,COOKIE數據使用了addslashes()函數. 如果網站空間關閉了 magic_quotes_gpc
設置, 那麼PHP就不會在敏感字元前加上反斜槓(\), 即允許表單所提交的內容含有敏感字元,
例如單引號(’)等等, 這就更容易讓駭客或者惡意使用者有機會利用SQL的注入漏洞發動攻擊.
當然現在很多具備安全意識的程式員或數據庫管理員都懂得如何防範SQL注入攻擊,他們通常
會在相應的程式代碼或應用環境中加強安全防範, 但是無論如何, 在系統層級加強安全防範,
始終都應該是LINUX系統管理員的不二責任.

請注意: PHP程式代碼中關於敏感字元的處理,可以用addslashes()來自動在敏感字元前添加
反斜槓, 也可以用函數stripslashes()來去掉反斜線. 另外, 許多數據庫本身也提供了針對
這種輸入數據的處理功能. 例如在PHP版本的MySQL操作函數中, 就有一個調用數據庫來處理
輸入數據的函數: mysql_real_escape_string(); 它可將特殊字符以及可能引起數據庫操作
出錯的字符轉義.

在Apache的httpd.conf中VirtualHost的相應設置方法:

php_admin_flag magic_quotes_gpc on
或者:
php_admin_value magic_quotes_gpc 1

================================================================================

================================================================================
7) allow_url_fopen和allow_url_include: 禁止讀取遠程文件
——————————————————————————–

allow_url_fopen = On                (允許打開URL文件,預設啟用)
allow_url_fopen = Off                (禁止打開URL文件)
allow_url_include = Off                (禁止引用URL文件,新版增加功能,預設關閉)
allow_url_include = On                (允許引用URL文件,新版增加功能)

allow_url_fopen 這個命令選項啟動了URL形式的fopen封裝協議, 使得PHP程式可以連接URL
對象(如遠端文件). 預定的封裝協議提供用ftp和http協議來連接遠程文件,一些擴展庫例如
zlib可能會註冊更多的封裝協議. allow_url_include預計是下一個PHP版本將要提供的功能,
用來分離fopen和include函數的遠端調用,現在PHP5.20已經提供了這個選項.

就性能方面來說,PHP所提供的方便的遠程調用確實簡化了很多應用, 但若是從安全角度來看,
允許引用(Include)URL遠端資源,使得PHP應用程式的漏洞變得更加容易被利用, 這種方便性
反而被很多安全研究人員視為一種漏洞(Remote URL Include vulnerabilities), 因此常常
被建議必須在php.ini配置中禁止使用.

PHP的開發者計劃在PHP6版本中提供allow_url_include,現在這個功能已經可以在PHP5.20版
中應用. 禁止allow_url_include解決了遠端引用(Include)的問題, 同時又讓我們還可以在
一般的情形下使用fopen去打開遠端的檔案, 而不必再牽連上打開include函數所帶來的風險.
因此在新版PHP中allow_url_fopen選項預設是打開的,而allow_url_include則預設是關閉的.

然而事實上若從系統角度來看,即使禁止了PHP的allow_url_fopen和allow_url_include功能,
其實也不能完全阻止遠端調用及其所帶來的安全隱憂,而且它們只是保護了標記為URL的句柄,
也就是說只能影響http(s)和ftp(s)的調用, 但對包含其他標記的遠端調用,例如對PHP5.2.0
新版所提供的php和data則無能為力,而這些調用一樣會導致注入風險,請參考如下代碼:

  // The following Include statement will
  // include and execute everything POSTed
  // to the server

  include "php://input";
?>

  // The following Include statement will
  // include and execute the base64 encoded
  // payload. Here this is just phpinfo()

  include "data:;base64,PD9waHAgcGhwaW5mbygpOz8+";
?>

當然在LINUX的系統層級上,還有別的辦法可用來防止遠端調用, 例如使用IPTABLES等防火牆
工具來保護系統,另外PHP應用程式也可以考慮採用curl來讀取遠程文件.

請注意: 只在PHP4.0.3之後的版本中才可以在php.ini配置文件中使用allow_url_fopen選項,
在PHP4.0.3以及之前的版本, 則只能在編譯時通過配置項 –disable-url-fopen-wrapper來
取消此特性. Windows下的PHP在4.3版本之前,相關函式: include, include_once, require,
require_once 不支援遠程文件連接,在PHP4.3版本之後才可以讓這類函式有遠端讀取的能力.

在Apache的httpd.conf中VirtualHost的相應設置方法:

php_admin_flag allow_url_fopen Off
php_admin_flag allow_url_include Off
或者:
php_admin_value allow_url_fopen 0
php_admin_value allow_url_include 0

================================================================================

================================================================================
8) Error handling and logging: 錯誤控制和日誌
——————————————————————————–

display_errors = On        (打開錯誤顯示)
display_errors = Off        (關閉錯誤顯示)

PHP缺省是打開錯誤資訊顯示的,如果把它改為關閉之後, 那麼當PHP函數執行時,其錯誤資訊
將不會再顯示給用戶,這樣能在一定程度上防止攻擊者從錯誤資訊得知腳本的物理位置,以及
一些其它有用的資訊,起碼給攻擊者的黑箱檢測造成一定的障礙.

如果PHP的錯誤資訊對我們自己有用,也可以設置成把它寫到日誌文件中去,例如:
log_errors = Off (PHP 默認是關閉錯誤日誌的)
log_errors = On (修改為打開並記錄錯誤日誌)

如果打開了日誌記錄,接著還需要指定日誌文件,即告訴PHP將錯誤記錄到那個文件中去:
;error_log = filename (默認被分號”;”所注釋,修改為如下)
error_log = /var/log/php_error.log
就是把filename改為指定文件”/var/log/php_error.log”, 這樣設置以後,所有的PHP錯誤都
將會寫到這個日誌文件?去。

在Apache的httpd.conf中VirtualHost的相應設置方法:

php_admin_flag display_errors Off
或者:
php_admin_value display_errors 0

為方便區分,可在每個用戶的VirtualHost中指定錯誤日誌文件:
ErrorLog logs/mydomain.com-error_log
================================================================================

################################################################################
PHP安全配置範例:
################################################################################
================================================================================

php.ini的安全設置範例:
——————————————————————————–

safe_mode = On
allow_url_fopen = Off
allow_url_include = Off
register_globals = Off
magic_quotes_gpc = On
display_errors = Off

disable_functions = shell_exec,system,exec,passthru,show_source,get_cfg_var
#或者,也可以考慮開放後兩個危險係數較低的函數:
disable_functions = shell_exec,system,exec,passthru

open_basedir = .

——————————————————————————–

VirtualHost的一個配置範例:
——————————————————————————–


    ServerAdmin [email]webmaster@mydomain[/email]
    DocumentRoot /home/hosting/mydomain/public_html
    ServerName mydomain.com
    ServerAlias www.mydomain.com
    php_admin_value safe_mode 1
    php_admin_value allow_url_fopen 0
    php_admin_value allow_url_include 0
    php_admin_value register_globals 1
    php_admin_value magic_quotes_gpc 1
    php_admin_value display_errors 0
    php_admin_value open_basedir /home/hosting/mydomain/
    ErrorLog logs/mydomain.com-error_log
    CustomLog logs/mydomain.com-access_log common

#或者:


    ServerAdmin [email]webmaster@mydomain[/email]
    DocumentRoot /home/hosting/hung25ucom/public_html
    ServerName mydomain.com
    ServerAlias www.mydomain.com
    php_admin_flag safe_mode On
    php_admin_flag allow_url_fopen Off
    php_admin_flag allow_url_include Off
    php_admin_flag register_globals On
    php_admin_flag magic_quotes_gpc On
    php_admin_flag display_errors Off
    php_admin_value open_basedir /home/hosting/mydomain/
    ErrorLog logs/mydomain.com-error_log
    CustomLog logs/mydomain.com-access_log common
——————————————————————————–
===============================================================================——————————————————————— ———–
最近更新 ( 週日, 31 五月 2009 00:14 )