PHP是廣泛使用的開源服務端腳本語言。通過HTTP或HTTPS協議,Apache Web服務允許用戶訪問文件或內容。服務端腳本語言的錯誤配置會導致各種問題。因此,PHP應該小心使用。以下是為系統管理員准備的,安全配置PHP的25個實踐事例。
用於下文的PHP設置樣例
下午列出的大部分操作,都是基於 root 用戶能在 bash 或其他現代 shell 上執行操作的假設。
$ php -v
樣例輸出
PHP 5.3.3 (cli) (built: Oct 24 2011 08:35:41) Copyright (c) 1997-2010 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
本文使用的操作系統
$ cat /etc/redhat-release
樣例輸出
Red Hat Enterprise Linux Server release 6.1 (Santiago)
#1:知彼
基於PHP的應用面臨著各種各樣的攻擊:
#2:減少內建的PHP模塊
執行下面指令可以查看當前PHP所編譯的模塊
$ php -m
樣例輸出:
[PHP Modules] apc bcmath bz2 calendar Core ctype curl date dom ereg exif fileinfo filter ftp gd gettext gmp hash iconv imap json libxml mbstring memcache mysql mysqli openssl pcntl pcre PDO pdo_mysql pdo_sqlite Phar readline Reflection session shmop SimpleXML sockets SPL sqlite3 standard suhosin tokenizer wddx xml xmlreader xmlrpc xmlwriter xsl zip zlib [Zend Modules] Suhosin
從性能與安全性的角度考慮,我建議使用PHP時減少不必要的模塊。例如上面的sqlite3是不必要的。那麼可以通過刪除或重命名/etc/php.d/sqlite3.ini文件來取消它:
# rm /etc/php.d/sqlite3.ini
或
# mv /etc/php.d/sqlite3.ini /etc/php.d/sqlite3.disable
有些模塊則只能通過使用重新編譯安裝PHP來移除。例如,從php.net下載PHP源碼後,使用下面指令編譯GD,fastcgi和MySQL支持:
./configure --with-libdir=lib64 --with-gd --with-mysql --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include --libexecdir=/usr/libexec --localstatedir=/var --sharedstatedir=/usr/com --mandir=/usr/share/man --infodir=/usr/share/info --cache-file=../config.cache --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --enable-fastcgi --enable-force-cgi-redirect
更多信息請查看:how to compile and reinstall php on Unix like operating system
#3:防止PHP信息洩漏
可以通過取消export_php,對PHP信息洩漏進行限制。編輯/etc/php.d/security.ini如下:
expose_php=Off
expose_php會在HTTP Header中添加服務器上,包括版本在內的PHP信息(例如X-Powered-By: PHP/5.3.3)。同時,PHP的全局統一標識符也會暴露。如果export_php啟用的話,可以通過下面命令查看PHP版本信息:
$ curl -I http://www.cyberciti.biz/index.php
樣例輸出:
HTTP/1.1 200 OK X-Powered-By: PHP/5.3.3 Content-type: text/html; charset=UTF-8 Vary: Accept-Encoding, Cookie X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;string-contains=wikiToken;string-contains=wikiLoggedOut;string-contains=wiki_session Last-Modified: Thu, 03 Nov 2011 22:32:55 GMT ...
建議同時隱藏Apache版本等信息:ServerTokens and ServerSignature directives in httpd.conf to hide Apache version
#4:最小化可載入的PHP模塊(動態Extension)
PHP支持“Dynamic Extensions”。默認情況下,RHEL會載入/etc/php.d/目錄下的所有Extension模塊。如需啟用或取消某一模塊,只需把/etc/php.d/目錄下配置文件把該模塊注釋掉。也可以把文件刪除或重命名該模塊的配置文件。為了最優化PHP的性能和安全性,應只啟用Web應用所需的Extension。例如,用下面命令取消GD模塊:
# cd /etc/php.d/ # mv gd.{ini,disable} # <span># mv gd.{disable,ini} # <span>display_errors=Off
確保把所有錯誤信息記錄到日志文件
log_errors=On error_log=/var/log/httpd/php_scripts_error.log
#6:禁止文件上傳
為安全考慮,如下編輯/etc/php.d/security.ini取消文件上傳
file_uploads=Off
如用戶的確需要上傳文件,那麼把它啟用,而後限制PHP接受的最大文件大小:
file_uploads=On # user can only upload upto 1MB via php upload_max_filesize=1M
#7:關閉遠程代碼執行
如果這個特性被啟動,PHP可以通過allow_url_fopen,在file_get_contents()、include、require中獲取諸如FTP或網頁內容這些遠程數據。程序員經常忘記了對用戶輸入進行過濾,而如果這些函數調用了這些數據,則形成了注入漏洞。在基於PHP的Web應用中,大量代碼中的注入漏洞都由此產生。可以通過編輯/etc/php.d/security.ini來關閉該特性:
allow_url_fopen=Off
除此之外,建議把allow_url_include也取消掉:
allow_url_include=Off
#8:啟用SQL安全模式
如下修改/etc/php.d/security.ini:
sql.safe_mode=On
當此特性被啟用,mysql_connect()和mysql_pconnect()會忽略傳入的所有參數。與此同時,你需要在代碼上做些相應的修改。第三方以及開源應用,如Wordpress,在sql.safe_mode下可能無法正常工作。同時建議關閉5.3.x版本的PHP的magic_quotes_gpc過濾,因為它簡單粗暴又沒效率。使用mysql_escape_string()以及自定義的過濾函數會更好一些
magic_quotes_gpc=Off
#9:控制POST的數據大小
HTTP POST通常作為請求的一部分,被客戶端用於向Apache Web服務器發送數據,如上傳文件或提交表單。攻擊者會嘗試發送超大的POST請求去消耗服務器的資源。如下編輯/etc/php.d/security.ini限制POST的最大大小:
; 在這裡設置一個靠譜的數值 post_max_size=1K
這裡設置了1K的最大大小。這個設置會影響到文件上傳。要上傳大文件,這個值需要比update_max_filesize大。
建議在Apache中限制可用的請求方法,編輯httpd.conf如下:
<Directory /var/www/html> <LimitExcept GET POST> Order allow,deny </LimitExcept> ## Add rest of the config goes here... ## </Directory>
#10:資源控制(DoS控制)
設置每個PHP腳本的最大運行時間。另外建議限制用於處理請求數據的最大時間,以及最大可用內存數。
# 單位:秒
max_execution_time = 30 max_input_time = 30 memory_limit = 40M
#11:為PHP安裝Suhosin高級保護系統
具體參考Suhosin項目頁:project page
#12:取消危險的PHP函數
PHP有大量可用於入侵服務器的函數,如使用不當則會成為漏洞。如下取消這些函數:
disable_functions =exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
#13:PHP Fastcgi / CGI – cgi.force_redirect管理
PHP可與Fastcgi協同工作。Fastcgi可以減少Web服務器的內存足跡(memory footprint),並改善PHP性能。可以參考這個來配置Apache2+PHP+FastCGI。在這個配置中,cgi.force_redirect會阻止用戶通過訪問URL來調用PHP。為安全考慮,啟用該特性:
; Enable cgi.force_redirect for security reasons in a typical *Apache+PHP-CGI/FastCGI* setup cgi.force_redirect=On
#14:PHP用戶與用戶組ID
mod_fastcgi是Apache Web服務的一個cgi模塊,可連接到外部的FASTCGI服務器。你需要確保PHP使用非root用戶運行。若其使用root或是UID小於100的用戶權限,它就可以訪問,乃至操作系統文件。通過Apache’s suEXEC或mod_suPHP,可在非特權用戶下執行PHP CGI。suEXEC可以是Apache調用CGI程序的user ID不同於運行Apache的user ID。如下:
# ps aux | grep php-cgi
樣例輸出:
phpcgi 6012 0.0 0.4 225036 60140 S Nov22 0:12 /usr/bin/php-cgi phpcgi 6054 0.0 0.5 229928 62820 S Nov22 0:11 /usr/bin/php-cgi phpcgi 6055 0.1 0.4 224944 53260 S Nov22 0:18 /usr/bin/php-cgi phpcgi 6085 0.0 0.4 224680 56948 S Nov22 0:11 /usr/bin/php-cgi phpcgi 6103 0.0 0.4 224564 57956 S Nov22 0:11 /usr/bin/php-cgi phpcgi 6815 0.4 0.5 228556 61220 S 00:52 0:19 /usr/bin/php-cgi phpcgi 6821 0.3 0.5 228008 61252 S 00:55 0:12 /usr/bin/php-cgi
可以通過spawn-fcgi來生成phpcgi用戶的遠程或本地FastCGI進程(前提是有這個用戶):
# spawn-fcgi -a 127.0.0.1 -p 9000 -u phpcgi -g phpcgi -f /usr/bin/php-cgi
現在可以配置Apache、Lighthttpd或Nginx Web服務調用運行在127.0.0.1:9000的FastCGI。
#15:限制PHP訪問文件系統
open_basedir會限制PHP的運行目錄,例如通過fopen()之類的函數可訪問的目錄。如果訪問的目錄不在open_basedir之內,PHP會拒絕該訪問。不要使用軟鏈接作為工作區。例如,只允許訪問/var/www/html而非/var/www、/tmp或/etc目錄:
; Limits the PHP process from accessing files outside ; of specifically designated directories such as /var/www/html/ open_basedir="/var/www/html/" ; ------------------------------------ ; Multiple dirs example ; open_basedir="/home/httpd/vhost/cyberciti.biz/html/:/home/httpd/vhost/nixcraft.com/html/:/home/httpd/vhost/theos.in/html/" ; ------------------------------------
#16:Session路徑
PHP Session用戶提供數據保存功能,以便後續訪問。這可以使應用可定制性更強,提升吸引力。所有Session相關的數據會被保存在session.save_path中。RHEL/CentOS/Fedora Linux的默認設置如下:
session.save_path="/var/lib/php/session" ; Set the temporary directory used for storing files when doing file upload upload_tmp_dir="/var/lib/php/session"
確認這個路徑在/var/www/html之外,且不可被其他系統用戶訪問:
# ls -Z /var/lib/php/
樣例輸出:
drwxrwx---. root apache system_u:object_r:httpd_var_run_t:s0 session
注:ls -Z會顯示SELinux的安全信息,如文件模式,user,group,安全信息,文件名等。
#17:保證PHP,軟件及操作系統更新到最新
維護Linux、Apache、PHP和MySQL服務器的一項重要工作是更新安全補丁。所有的PHP安全更新應盡快進行審查並更新。可使用如下命令(如果通過包管理器來安裝PHP):
# yum update
或
# apt-get update && apt-get upgrade
可以配置Red Hat / CentOS / Fedora Linux通過Email發送yum的包更新提醒,或是Debian / Ubuntu Linux下的apticron發送提醒。又或通過cron計劃任務進行更新。
注:查看php.net以獲取最新的PHP版本信息
#18:限制文件及目錄訪問
確認以Apache或www這種非root用戶運行Apache。/var/www/html目錄下的owner也應是非root用戶:
# chown -R apache:apache /var/www/html/
DocumentRoot下的文件應禁止運行或創建。設置該目錄下的文件權限為0444(只讀):
# chmod -R 0444 /var/www/html/
設置該目錄下的所有文件夾權限為0445
# find /var/www/html/ -type d -print0 | xargs -0 -I {} chmod 0445 {}
#19:Apache、PHP、MySQL配置文件的寫入保護
使用chattr命令給這些配置文件加上寫入保護:
# chattr +i /etc/php.ini # chattr +i /etc/php.d/* # chattr +i /etc/my.ini # chattr +i /etc/httpd/conf/httpd.conf # chattr +i /etc/
同樣可以為/var/www/html目錄加上寫入保護
# chattr +i /var/www/html/file1.php# chattr +i /var/www/html/
#20:使用Linux安全拓展(如SELinux)
Linux有各種安全方案來防止服務程序的錯誤配置或漏洞。盡可能使用SELinux或其他Linux安全方案限制網絡和程序。例如,SELinux為Linux內核或Apache Web服務提供不同的安全策略。使用下面命令列出所有Apache保護信息:
# getsebool -a | grep httpd
樣例輸出:
allow_httpd_anon_write --> off allow_httpd_mod_auth_ntlm_winbind --> off allow_httpd_mod_auth_pam --> off allow_httpd_sys_script_anon_write --> off httpd_builtin_scripting --> on httpd_can_check_spam --> off httpd_can_network_connect --> off httpd_can_network_connect_cobbler --> off httpd_can_network_connect_db --> off httpd_can_network_memcache --> off httpd_can_network_relay --> off httpd_can_sendmail --> off httpd_dbus_avahi --> on httpd_enable_cgi --> on httpd_enable_ftp_server --> off httpd_enable_homedirs --> off httpd_execmem --> off httpd_read_user_content --> off httpd_setrlimit --> off httpd_ssi_exec --> off httpd_tmp_exec --> off httpd_tty_comm --> on httpd_unified --> on httpd_use_cifs --> off httpd_use_gpg --> off httpd_use_nfs --> off
取消Apache cgi支持可以輸入:
# setsebool -P httpd_enable_cgi off
詳細參考:Red Hat SELinux guide
#21:安裝Mod_security
ModSecurity是一個開源的入侵檢測和防范的Web應用引擎。安裝mod_security可以保護Apache和PHP應用免受XSS和其他攻擊:
## A few Examples ## # Do not allow to open files in /etc/ SecFilter /etc/ # Stop SQL injection SecFilter "delete[[:space:]]+from" SecFilter "select.+from"
#22:如有可能,在Chroot Jail下運行Apache / PHP
在Chroot Jail下運行Apache / PHP可以最小化可能受到的損失,使其局限於文件系統下的一小塊。可以使用一般的chroot來配置Apache:chroot kind of setup with Apache。不過我建議使用FreeBSD jails、XEN,KVM或OpenVZ虛擬化。
#23:使用防火牆限制傳出連接
攻擊者會使用wget之類的工具從你的Web服務器下載文件。使用iptables來阻擋Apache用戶的傳出連接。ipt_owner模塊會為本地數據包的生成者分配不同角色。它只對OUTPUT chain有效。下面指令允許vivek用戶通過80端口進行外部訪問:
/sbin/iptables -A OUTPUT -o eth0 -m owner --uid-owner vivek -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
下面的樣例則是阻擋所有Apache用戶的傳出連接,只允許smtp服務及spam識別API服務通過:
# .... /sbin/iptables --new-chain apache_user /sbin/iptables --append OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT /sbin/iptables --append OUTPUT -m owner --uid-owner apache -j apache_user # allow apache user to connec to our smtp server /sbin/iptables --append apache_user -p tcp --syn -d 192.168.1.100 --dport 25 -j RETURN # Allow apache user to connec to api server for spam validation /sbin/iptables --append apache_user -p tcp --syn -d 66.135.58.62 --dport 80 -j RETURN /sbin/iptables --append apache_user -p tcp --syn -d 66.135.58.61 --dport 80 -j RETURN /sbin/iptables --append apache_user -p tcp --syn -d 72.233.69.89 --dport 80 -j RETURN /sbin/iptables --append apache_user -p tcp --syn -d 72.233.69.88 --dport 80 -j RETURN ######################### ## Add more rules here ## ######################### # No editing below # Drop everything for apache outgoing connection /sbin/iptables --append apache_user -j REJECT
#24:查看並審查日志
查看Apache日志文件:
# tail -f /var/log/httpd/error_log # grep 'login.php' /var/log/httpd/error_log # egrep -i "denied|error|warn" /var/log/httpd/error_log
查看PHP日志文件:
# tail -f /var/log/httpd/php_scripts_error.log # grep "...etc/passwd" /var/log/httpd/php_scripts_error.log
查看日志文件可以讓你知道服務器正在承受何種攻擊,並分析當前安全級別是否足夠。啟用審查服務用於系統審查,可審查SELinux時間,驗證事件,文件修改,賬號修改等。建議使用Linux System Monitoring Tools來監控Web服務器。
#25:把服務分離到不同的服務器或虛擬機
對於比較龐大的安裝配置,建議把運行、數據庫、靜態與動態內容分離到不同的服務器
/////////////// / ISP/Router / ////////////// \ | Firewall \ | +------------+ | LB01 | +------------+ +--------------------------+ | | static.lan.cyberciti.biz | +-----------------+--------------------------+ | phpcgi1.lan.cyberciti.biz| +--------------------------+ | phpcgi2.lan.cyberciti.biz| +--------------------------+ | mysql1.lan.cyberciti.biz | +--------------------------+ | mcache1.lan.cyberciti.biz| +--------------------------+
在不同的服務器或虛擬機下運行不同的網絡服務,這可以減少被入侵對其他服務的影響。例如,一個攻擊者入侵了Apache,那就可以訪問同一服務器下的其他服務(如MySQL,email服務等)。但在上述例子中則不會: