關於nginx/php-fpm/apache mod_php中的安全配置,可參考我之前的另外一個文章:
《正確設定nginx/php-fpm/apache 提高網站安全性 防止被掛木馬》
http://zhangxugg-163-com.iteye.com/blog/1171572
一、基礎知識準備
在任何配置開始前,一定要搞清楚這幾個概念,如果這幾個概念沒有徹底弄清楚,就無法理解後續的配置的意義了, 有經驗的系統管理員可跳過。
1. 網站檔案所有者賬戶是什麼,檔案的許可權又是什嗎?
2.nginx, php-fpm, apache用什麼賬戶運行?
3.網站的哪些目錄和檔案是可寫的?這裡的可寫,是指對於nginx ? php-fpm ? 還是apache ?
4.如何禁止php代碼被執行?
Linux的最基本的許可權是:檔案的所有者,對其所屬所有檔案都有任何許可權(root許可權不受限制),讀寫執行許可權分別被標識為4 2 1
我看到最多的是,一些系統管理員為了省事,讓php-fpm/apache以網站檔案所有者,這樣方便php程式在任何位置組建檔案, 但這樣配置卻可能造成嚴重的安全問題。
上面幾個問題的回複:
1.在linux目錄中,使用ls -l命令即可查看目前的目錄所有者,第3、4列分別檔案所屬於的使用者/組,示,這是yii架構的protected目錄中的檔案清單
我們可以看到,檔案所有者和組是ftp, runtime目錄許可權是任何人有讀寫執行許可權.
2.網站中,往往需要可寫目錄,用於運行時組建檔案(緩衝、靜態檔案、附件上傳等),所以這個可寫,是針對php程式本身而言的,nginx+php-fpm模式下,nginx只是負責把請求轉寄給php-fpm進程,所以最終的組建檔案是由php-fpm產生的。但是Apache mod_php卻有所不同,mod_php作為apache的一個模組,其許可權繼承自apache, 故說成apache的可寫目錄,也不為錯。
可以用命令 ps aux | egrep 'nginx|php-fpm|apache' 查看nginx/php-fpm/apache運行賬戶
3.與開發人員溝通,瞭解可寫目錄及其意義
4.如果禁止php代碼被執行?可能由於程式碼漏洞,木馬檔案被上傳到了附件目錄,你可能認為在linux中去掉這個目錄的執行許可權就行了?
chmod a-x -R uploadfile
但這樣就導致apache無法讀取此目錄的檔案,導致附件檔案無法被訪問了。另一方面,這種方式也是錯誤的。即便是去掉附件檔案本身的執行許可權(普通檔案本身,是沒有執行許可權),也是行不通的,要先瞭解php的運行機制。
php的常用運行方式有以下幾種:
A . 以nginx+php-fpm的方式運行
B. 以apache mod_php方式運行
C. 命令列下,使用php 方式運行
D. 在php指令碼第一行添加:#!/usr/local/php/bin/php, 並給此檔案添加執行許可權,就可以把它當成一shell指令碼運行了。這種檔案並不常見。
這幾種方式中,只有最後一種方式才需要執行許可權,其它幾種方式下,只在php解譯器進程對php指令碼有讀取許可權,即可運行。關於如何禁止php指令碼的執行,可參閱作者的上述文章。
二、safa_mode
php的安全模式備受爭議,因為它涉及系統配置,而且它是使用了windows的配置思想,在Linux環境中並不適合。簡單地說,開啟safe_mode後,php進程本身只允許開啟屬於它自己的指令檔。也就是說,在多網站環境中,我們必須為每個網站建立一個使用者,並讓php進程以對應的賬戶許可權運行, 這樣它可以對自己的網站檔案有最高許可權,但對其它使用者的檔案,則沒有任何許可權。聽起來不錯,但事實上這個配置起來的工作量相當繁瑣而且極容易出錯:
1. 如果網站很多,需要建立大量賬戶,將將網站目錄分別授權給這些使用者
2.php-fpm需要建立大量的進程池,並指定不同的賬戶身份, 造成資源的極大浪費
3.apachce不能配置多個運行賬戶,只能指定一個。
4.共用目錄(如/tmp, /dev/shm等需要單獨指定),並不是一件容易的事。
可見,正因為safe_mode配置如此繁瑣, php新版將取消safe_mode的支援,PHP手冊上已經描述很清楚:
5. 如果網站要遷移到其它機器,使用者賬戶也需要完全遷移,這也是很繁瑣的工作量。
安全模式自 PHP 5.3.0 起廢棄並將自 PHP 5.4.0 起移除。
所以,依賴安全模式的方式,在php新版本中將不會被支援,筆者編寫這個文章時,php 7.0.2已經發布。
三、為什麼要用open_basedir
open_basedir 用於限制php進程可以開啟的目錄首碼,一般來說,php程式除了需要讀取本網站的檔案外,還往往會用到/tmp /dev/shm目錄。
假設網站位於 /data/wwwroot/site.cn,在運行時要用到/tmp, /dev/shm, /proc目錄,那麼可以在php.ini中這樣設定
open_basedir = /data/wwwroot/site.cn:/tmp:/dev/shm:/proc
如果php中使用include, require, fopen, gzopen等函數載入其它目錄檔案,就會報錯:
require_once(): open_basedir restriction in effect. File(file.php) is not within the allowed path(s): (PATH)
Warning: require_once(file.php): failed to open stream: Operation not permitted in
Fatal error: require_once(): Failed opening required ...
這個參數在 php 5.2.3時只能在php.ini中配置,無法在運行時配置。php 5.2.3時,可以在任何位置設定。
顯然,針對一個有大量網站的主機, 使用多個php.ini的方式,就非常難以配置。方法有兩種:
1.在php-fpm.conf設定檔中,為每個進程池指定不同的限定目錄
[www]
php_admin_value[open_basedir] = /var/www/www.example.com:/usr/share/php5:/tmp:/usr/share/phpmyadmin:/etc/phpmyadmin
php_admin_value是指此值一旦設定,無法在運行時被修改。但這種方法,卻並不被推薦的,因為每個網站都需要建立一個進程池,有100個網站,就需要建立100個進程池,而且這些php進程,無法互相使用,只能是網站獨享,造成資源極大浪費。
2.nginx通過fastcgi協議和php-fpm通訊時,可以指定一些參數,用於在請求開始前,修改php的配置參數, 這是最為方便有效方法,強制推薦
fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/:/dev/shm";
這是php-fpm特有的功能,表示從fastcgi用戶端可以接收一些參數指定
由於$document_root變數的引入,我們就可以直接大量設定了。
這樣一來,所有網站可共用同一個進程池,同時又解決了分別限制訪問目錄的問題。我們可以把這個指令加入到nginx的fastcgi_params設定檔中,這樣所有網站就立即生效,解決了難題
可以用以下php代碼測試一下
echo ini_get('open_basedir');
ini_set('open_basedir', '/etc/');
echo ini_get('open_basedir');
可見已經設定為所期望的限制,並無法在運行時進行修改的。 通過這個舉一反三,我們可以在nginx根據需要指定php的各種配置了