PHP最初是被稱作Personal Home Page,後來隨著PHP成為一種非常流行的腳本語言,名稱也隨之改變了,叫做Professional HyperText PreProcessor。以PHP4.2為例支持它的WEB服務器有:Apache, Microsoft Internet information Sereve, Microsoft Personal web Server,AOLserver,Netscape Enterprise 等等。
PHP是一種功能強大的語言和解釋器,無論是作為模塊方式包含到web服務器裡安裝的還是作為單獨的CGI程序程序安裝的,都能訪問文件、執行命令或者在服務器上打開鏈接。而這些特性都使得PHP運行時帶來安全問題。雖然PH P是特意設計成一種比用Perl或C語言所編寫的CGI程序要安全的語言,但正確使用編譯時和運行中的一些配置選項以及恰當的應用編碼將會保證其運行的安全性。
一、安全從開始編譯PHP開始。
在編譯PHP之前,首先確保操作系統的版本是最新的,必要的補丁程序必須安裝過。另外使用編譯的PHP也應當是最新的版本,關於PHP的安全漏洞也常有發現,請使用最新版本,如果已經安裝過PHP請升級為最新版本:4.2.3
相關鏈接:http://security.e-matters.de/advisories/012002.html
安裝編譯PHP過程中要注意的3個問題:
1、只容許CGI文件從特定的目錄下執行:首先把處理CGI腳本的默認句柄刪除,然後在要執行CGI腳本的目錄在http.conf 文件中加入ScriptAlias指令。
#Addhadler cgi-script .cgi
ScriptAlias /cgi-bin/ "/usr/local/apache/cgi-bin/"
<Directory "/usr/local/apache/cgi-bin'>
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
<Directory "/home/*/public_html/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
SriptAlias的第一個參數指明在Web中的可用相對路徑,第二個參數指明腳本放在服務器的目錄。應該對每個目錄
別名都用Directory,這樣可使得除系統管理員之外的人不知道Web服務器上CGI腳本的清單。
Directory允許用戶創建自己的CGI腳本。也可用SriptAliasMatch,但Directory更容易使用。 允許用戶創建自己
CGI腳本可能會導致安全問題,你可能不希望用戶創建自己的CGI。 Apache默認配置是注釋掉cgi—script的處理句柄,但有/cgi-bin目錄使用SriptAlias和Directory指令。 你也可禁止CGI執行,但仍允許執行PHP腳本。
2.把PHP解析器放在web目錄外
把PHP解析器放在Web目錄樹外是非常重要的做法。這樣可以防止web服務器對PHP的解析器的濫用。特別是
不要把PHP解析器放在cgi-bin或允許執行CGI程序的目錄下。然而,使用Action解析腳本是不可能的,因為用Action指令時,PHP解析器大多數要放在能夠執行CGI的目錄下只有當PHP腳本作為CGI程序執行時,才能把PHP解析器放在Web目錄樹之外。
如果希望PHP腳本作為CGI程序執行(這們可以把PHP解析器放在Web目錄樹之外),可以這樣:
( 1)所有的PHP腳本必須位於能執行CGI程序的目錄裡。
( 2)腳本必須是可執行的(僅在UNIX/Linux機器裡)。
(3)腳本必須在文件頭包括PHP解析器的路徑。
你可用下面命令使PHP腳本為可執行:
#chmod +x test.php4
這樣使在當前目錄下的文件名為test.PhP4的腳本變為可執行。 下面是一個能作為CGI程序運行的PHP腳的小例子。
#!/usr/local/bin/php
echo "This is a my small cgi program”
3. 按Apache模塊方式安裝:
當將PHP作為Apache模塊使用時,它將繼承Apche的用戶權限(一般情況下用戶為“nobody”)。這一點對於安全性和
驗證有不少影響。例如,使用PHP訪問數據庫,除非數據庫支持內建的訪問控制,將不得不設置數據庫對於用戶“nobody”
的可訪問權限。這將意味著惡意的腳本在沒有訪問用戶名和密碼,也能訪問並修改數據庫。通過Apache驗證來保護數據不被暴露,或者也可使用LDAP、.htaccess文件等設計自己的訪問控制模型,並在PHP腳本中將此代碼作為其中部分引入。 通常,一旦安全性建立,此處PHP用戶(此情形即Apache用戶)就風險大大降低了,會發現PHP護現在已被封禁了將可能的染毒文件寫入用戶目錄的能力。 此處最常犯的安全性錯誤是賦予Apache服務器根(root)權限。 將Apache用戶權限提升到根權限是極端危險的。可能會危及整個系統,因此要小心使用sudo,chroot安全隱患大的命令等。除非你對安全有絕對的掌握,否則不要讓其以ROOT權限運行。
二、讓PHP的使用更安全。
1、以安全模式運行PHP
以安全模式運行PHP是使PHP腳本安全使用的好方法,特別是在允許用戶使用自己開發的PHP腳本時。使用安全模式會使PHP在運行函數時檢查是否存在安全問題。 include、readfile、fopen、file、unlink、rmdir等等:被包含的文件或者該文件所在目錄的所有者必須是正在運行的腳本的所有者; Exec、System、Passthm等等:要執行的程序必須位於特定的目錄(默認為/usr/local/php/bin)。編譯PHP時可以用—with-exe-dir選項設定這個值。
Mysql—Connect:這個函數用可選的用戶名連接MySQL數據庫。在安全模式下,用戶名必須是當前被執行的腳本的所有者,或運行httpd的用戶名(通常是nobody)。
HTTP Authentication:包含HTTP驗證代碼腳本所有者的用戶ID(數字型)會自動加到驗證域。這樣可以防止有人通過抓取密碼的程序來欺騙同一個服務器上的HTTP驗證腳本。
2、使用 用戶識別和驗證
有時需要唯一地確認一個用戶。用戶通常由請求和響應系統確認。用戶名/口令組合就是這種系統的一個很好的例子,比如系統要求給出A1i的口令,響應的是Ali的口令。這樣驗證是因為只有Ali才知道這個口令。
(1)服務器端用戶驗征
這是用於服務端上對PHP程序要求最小的驗證方法。只要讓Apache來管理對用戶的驗證就行了。
AuthName "Secret page" # The realm
AuthType Basic
# The password file has been placed outside the web tree
AuthUserFile /home/car2002/website.pw
<LIMIT GET POST>
require valid-user
</LIMIT>
你需要把上述文件(文件名為.htaccess)放在需要保護的地方。用Apache的htpasswd程序,可以建立包含用戶名和口令組合的文件。把這個文件放在Web目錄樹之外,只讓該文件的擁有者查看和修改這個文件。當然,Web服務器必須能夠讀取這個文件。
如果想讀取被保護的目錄,Web服務器要求浏覽器提供用戶名和密碼。浏覽器彈出對話框,用戶可以輸入他們的用戶名和密碼。如果用戶名和密碼與口令文件中相符合,就允許用戶讀取被保護的頁面;反之,將得到錯誤頁面,告訴用戶沒有通過驗證。被保護的域會顯示出來以便用戶知道輸入那個用戶名和密碼。
(2)在PHP中進行用戶識別和驗證
和在Apache服務器端進行用戶識別和驗證相比,在PHP進行用戶識別和驗證有以下優點:
A、可注銷。
B、可失效。如用戶登錄後40分鐘沒有浏覽你的網站,你可強制他們重新通過驗證。
C、可定制。
D、可基於數據庫。你可以用保存在各種各樣的數據庫裡的數據來驗證用戶,並且記錄訪問者訪問網站的詳細日志。
E、可用於每個頁面。你可在每個頁面上決定是否需要驗證。
F、你也可以使浏覽器彈出對話框。下面的例子顯示了怎樣從,MySQL數據庫中檢索用名和口令:讓用戶填人用戶名和口令。
<?
if(!isset($PHP_AUTH_USER)) {
Header("WWW-authenticate: basic realm=\"restricted area\"");
Header( "HTTP/I.0 401 Unauthorized");
echo "You failed to provide the correct password...\n";
exit;
} else {
mysql_select_db("users") ;
$user_id = strtolower($PHP^AUTH_USER);
$result = mysql_query("SELECT password FROM users " .
"WHERE username = '$username'") ;
$row = mysql_fetch_array($result) ;
if ($PHP_AUTH_PW != $row["password"]) {
Header( "WWW-authenticate: basic realm=\"restricted area\"
Header( "HTTP/I.0 401 Unauthorized");
echo "You failed to provide the correct password...\n" ;
exit;
}
}
?>
Only users with a working username/password combination can see this
(3) 檢測IP地址
一般人們普遍認為一個IP地址唯一地確定一個訪問者。但實際上並不是這樣的。代理服務器可用相同的IP地址發送不同用戶的請求。另外IP地址的盜用也普遍存在。檢測 IP地址有它們的用處,但相當有限。例如你是一個論壇版主,你發現某個用戶粘貼一些不健康的、違法的內容。你可以找到他的IP地址,把從這個IP連進來的用戶逐出論壇。使用下面一行命令將會得到某個特定請求的源IP地址:
# ip = $REMOTE_ADDR
4、使用PHP加密技術
在PHP中,加密技術主要用來加密信息、產生校驗和和摘要。使用加密技術可大大地增強安全性能。 這裡只講述使用加密技術的一些概念。如果你想進一步了解,應參考一些好的加密技術資料。加密技術的標准是Bmce Schneier的應用加密技術,非常值得一讀。他的網站(www.counterpane.com/labs.html )是在互聯網上查找加密技術資料的好起點。數據加密是一個非常復雜的話題,這裡只簡單介紹一下。
PHP中大多數的加密函數由mcrypt庫和mhash庫提供。你需要在系統中裝上這兩個庫,在編譯時加上--ith-mcrypt和--ith-hash選項。PHP從 3.013版本開始支持mcrypt庫。
5、使用具有SSL技術
SSI是英文Server Side Includes的縮寫。使用具有SSL(安全套接字協議層)功能的web服務器,可以不用改變一行代碼而提高網站的安全性能。SSI使用加密方法來保護web服務器和浏覽器之間的信息流。SSL不僅用於加密在互聯網上傳遞的數據流,而且還提供雙方身份驗證。這樣,你就可以安全地在線購物而不必擔心別人矢隨你的信用卡的信息。這種特性使得SSL適用於那些交換重要信息的地方,像電子商務和基於Web的郵件。
SSL使用公共密鑰加密技術,服務器在連接結束時給客戶端發送公用密鑰用來加密信息,而加密的信息只有服務器用它自己持有的專用密鑰才能解開。客戶端用公用密鑰加密數據,並且發送給服務端自己的密鑰,以唯一確定自己,防止在系統兩端之間有人冒充服務端或客戶端進行欺騙。
加密的HTTP連接用443端口號代替80端口號,以區別於普通的不加密的HTTP。客戶端使用加密HTTP連接時會自動使用443端口而不是80端口。這使得服務端更容易作出相應的響應。
在Apache服務器下,可以通過直接編輯服務器配置文件或者在需要使用SSI的目錄中創建.htaccess文件來啟動SSI。登錄到服務器,找到配置文件的存放目錄,使用文字編輯器打開文件srm.conf,找到以下幾行:
# If you want to use server side includes, or CGI outside
# ScriptAliased directories, uncomment the following lines.
#AddType text/x-server-parsed-html .shtml
#AddType application/x-httpd-CGI .CGI
將以AddType開頭的兩行並且去掉每一行最前面的"#"符號即可。保存所做的修改,然後再打開文件access.conf。
<Directory /usr/local/etc/httpd/htdocs>
# This may also be "None", "All", or any combination of "Indexes",
# "Includes", or "FollowSymLinks"
Options Indexes FollowSymLinks
</Directory>
將其中的Options Indexes FollowSymLinks改為:Options Indexes FollowSymLinks Includes 即可。
6、使用Apache的suEXEC機制
通常CGI程序或PHP腳本只能以啟動web服務器的用戶權限來運行(通常為www或nobody),這樣會出現的情況之一是可以讀寫和修改由另一個用戶的CGI和PHP腳本生成的文件(如腳本和密碼文件)。也可能使用戶可以連接到其他用戶的數據庫,但這與數據庫的配置有關。如MySQL的默認配置便是允許的,但可以通過強制數據庫進行口令驗證來彌補此不足。PHP的safe—mode減少了這些問題,但所有的腳本仍然以相同的用戶標識運行。Apache可以解決這個問題。suEXEC(在執行前改變用戶標識)是一個小工具,允許以任意用戶標識運行CGI程序,當然也包括PHP腳本,但根用戶除外。而且可以和UseDir和VirtualHost項一起使用。
所以suEXEC也叫CGI封裝。這意味著在腳本運行之前它需要通過一系列規定的安全檢查。隨Apache2。0版發布的suEXEC有26個檢查點。suEXEC能解決一些安全問題,同時允許用戶開發和更安全地執行自己的腳本。但是suEXEC會降低服務性能,因為suEXEC只能運行在CGI版本的PHP上,而CGI版本比模塊版本運行速度慢。原因是模塊版本使用了線程,而使用CGI版本的是進程。在不同線程之間的環境轉換和訪問公用的存儲區域顯然要比在不同的進程之間要快得多。使用suEXEC的另外一個問題是它增加了編寫和使用PHP腳本的難度。你要確保腳本能通過suEXEC的檢驗。否則,你的腳本不會被執行。我們建議在你對安全性能要求比較高時使用suEXEC ,為此你還要以犧牲速度為代價。
7、創建安全的PHP腳本
有很多編程技巧使PHP腳本更安全地運行。其中最重要的一條是使用一些安全常識。運行PHP比運行CGI腳本更安全,但它仍然有許多出現錯誤的地方。轉換到安全運行模式能夠限制出錯所產生的結果。如果你的PHP腳本中有錯誤,可能會被人找到並且利用它來破壞站點甚至數據庫。所以經常備份也是必要的。
(1)安全設置軟件
基於Web的應用程序,如在線目錄,通常都在無人密切監視的情況下運行。如果發生錯誤時,你不可能立即采取行動。通常訪問者最先注意到所發生的問題,你應該使他們很容易地報告所發生的問題。更進一步,可以由構成這個網站的腳本來跟蹤這些問題。例如,你的訪問者可能做一些你想不到的事情。也可能你對於重要函數所返回值沒有檢查,腳本可能會以不可預料的方式運行。
寫出更加安全的程序,就可以避免這些問題。例如你應該檢查數據庫函數的返回值,如果數據庫崩潰,顯示給用戶的應該是出錯的信息頁面而不是滿屏幕的錯誤。你甚至可以讓腳本在發生嚴重問題,如數據庫崩潰、硬盤空間已滿的時候自動通知你。你也應該檢查從用戶傳來的所有數據。顯然後者更重要。 如果你的程序能夠應付各種錯誤,那麼你的程序不僅更加可靠,而且可以花更少的時間來維護。這些時間可大大彌補你開發程序時所花的額外時間。
(2)存儲和交換敏感信息
顯然,你應盡量避免在互聯網上以GET、POST、cookie或URL編碼的形式傳遞敏感信息,這樣使信息很容易被竊取。使用支持SSL的web服務器能夠做到這一點,因為它加密站點和訪問者浏覽器之間所有的信息流。
如果你沒有支持SSL的Web服務器,那麼你需要其他的辦法。比如沒有必要總是發送數據到浏覽器;把數據保存在數據庫中,只向浏覽器發送關鍵字,這樣也很容易查找到所需要的數據;並以加密的形式發送所有的數據等等。實現這種功能的最簡單的辦法是使用Session。 PHP4支持本地化的Session功能,PHP3則要使用PHPMB庫。
HTTP 協議是一種無狀態協議,它不負責為好連接的狀態信息,因此無法跟蹤客戶端的各種信息,Session的出現改變這一狀況。當用戶浏覽一個支持Session功能的CGI腳本時,在他離開這個網頁前可以將用戶信息保存在同意Session ID之下,也就是可以在不同的網頁之間偕同存取用戶信息。
如果不使用PHP的安全模式或在suEXEC下以CGI方式運行PHP,那麼監視你的文件的內容就不可能實現。此時唯一防止別人讀取數據的方法是盡快把數據保存到數據庫中。
(3)檢查用戶輸入
Per1語言有個特性叫污點檢測(taint checking)。當污點檢測生效時,即使沒有發生重大錯誤,你也不能運行含有可疑變量的函數。一個變量,當它的值是用戶提供數據的一部分或全部時就變成可疑的了,因為這些數據被認為是不安全的。這樣可提高系統安性。 PHP沒有這個特性,但PHP有escapeshellcmd函數,可以達到同樣的效果。另一個不讓用戶濫用腳本的方法是只允許使用經過嚴格檢查的輸入。
(4)使用最新的PHP版本 4.2.xx
在很長一段時間內,PHP作為服務器端腳本語言的最大賣點之一就是會為從表單提交的值自動建立一個全局變量。在PHP 4.1中,PHP的制作者們推薦了一個訪問提交數據的替代手段。在PHP 4.2中,他們取消了那種老的做法。在PHP 4.1中,添加了一組特殊數據以訪問外部數據。這些數組可以在任何范圍內調用,這使得外部數據的訪問更方便。在PHP 4.2中,register_globals被默認關閉以鼓勵使用這些數組以避免無經驗的開發者編寫出不安全的PHP代碼。作出這樣的變化是出於安全性的考慮的。
三、總結
徹底安全的系統從理論上講不可能,因此我們所指安全性只是在代價與可用性間作平衡。若是用戶提交的每一個變量都要求有生物學驗證(如指紋鑒定),則將獲得極高水平的可靠性。但是也會造成用戶填寫一個表格就要幾十分鐘。這時用戶就會采取繞過安全驗證的方法。一個系統的可靠性只能由整個鏈條中最薄弱的環節來決定。在任何安全系統裡面,人是最脆弱的連接,單單技術本身不能讓系統安全。
PHP 還處在不斷發展的過程中,你需要經常關注他的安全信息。這裡筆者推薦你經常關注安全焦點(www.security-focus.com )和Packetstorm(www.packetstorm.com )。