寫給系統管理員的25個PHP安全實踐

jobbole發表於2013-12-26

  PHP是廣泛使用的開源服務端指令碼語言。通過HTTP或HTTPS協議,Apache Web服務允許使用者訪問檔案或內容。服務端指令碼語言的錯誤配置會導致各種問題。因此,PHP應該小心使用。以下是為系統管理員準備的,安全配置PHP的25個實踐事例。

  用於下文的PHP設定樣例

  • DocumentRoot:/var/www/html
  • 預設Web服務:Apache(可以使用Lighttpd或Nginx代替)
  • 預設PHP配置檔案:/etc/php.ini
  • 預設PHP Extensions配置目錄:/etc/php.d/
  • PHP安全配置樣例檔案:/etc/php.d/security.ini(需要使用文字編輯器建立這個檔案)
  • 作業系統:RHEL / CentOS / Fedora Linux(指令應該可以在所有其他Linux發行版,如Debian / Ubuntu,或是Unix-like的作業系統,如OpenBSD / FreeBSD / HP-UX下正常執行)
  • PHP服務的預設TCP/UDP埠:none

  下午列出的大部分操作,都是基於 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的應用面臨著各種各樣的攻擊:

  • XSS:對PHP的Web應用而言,跨站指令碼是一個易受攻擊的點。攻擊者可以利用它盜取使用者資訊。你可以配置Apache,或是寫更安全的PHP程式碼(驗證所有使用者輸入)來防範XSS攻擊
  • SQL隱碼攻擊:這是PHP應用中,資料庫層的易受攻擊點。防範方式同上。常用的方法是,使用mysql_real_escape_string()對引數進行轉義,而後進行SQL查詢。
  • 檔案上傳:它可以讓訪問者在伺服器上放置(即上傳)檔案。這會造成例如,刪除伺服器檔案、資料庫,獲取使用者資訊等一系列問題。你可以使用PHP來禁止檔案上傳,或編寫更安全的程式碼(如檢驗使用者輸入,只允許上傳png、gif這些圖片格式)
  • 包含本地與遠端檔案:攻擊者可以使遠端伺服器開啟檔案,執行任何PHP程式碼,然後上傳或刪除檔案,安裝後門。可以通過取消遠端檔案執行的設定來防範
  • eval():這個函式可以使一段字串如同PHP程式碼一樣執行。它通常被攻擊者用於在伺服器上隱藏程式碼和工具。通過配置PHP,取消eval()函式呼叫來實現
  • Sea-surt Attack(Cross-site request forgery,CSRF。跨站請求偽造):這種攻擊會使終端使用者在當前賬號下執行非指定行為。這會危害終端使用者的資料與操作安全。如果目標終端使用者的賬號用於管理員許可權,整個Web應用都會收到威脅。

  #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 style="text-decoration: underline;">/sbin/service httpd restart</span>

  啟用則是:

# mv gd.{disable,ini}
 # <span style="text-decoration: underline;">/sbin/service httpd restart</span>

  #5:記錄所有PHP錯誤

  不要把PHP錯誤資訊輸出給所用使用者。編輯/etc/php.d/security.ini,如下修改:

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 suEXECmod_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服務等)。但在上述例子中則不會:

  • static.lan.cybercity.biz – 使用lighttpd或nginx存放js/css/images等靜態資源
  • phpcgi1.lan.cyberciti.biz和phpcgi2.lan.cyberciti.biz – Apache Web服務+PHP,用於生成動態內容
  • mysql1.lan.cyberciti.biz – MySQL資料庫服務
  • mcache1.lan.cyberciti.biz – Memcached服務(MySQL的快取記憶體系統)。它使用libevent或epoll來適應任意連線數。而且它使用的是非阻塞網路IO。
  • LB01 – 一個Nginx伺服器,用於Web及Apache前端的反向代理。所有的訪問連線會通過nginx代理服務,被直接處理或分發到相應的Web伺服器。LB01提供簡單的負載均衡。

  原文連結: Nix Craft   翻譯: 伯樂線上 - Kroderia

相關文章