第14章 PHP安全
PHP的語法過於靈活,這也給安全工作帶來了一些困擾。同時PHP也存在很多歷史遺留的安全問題。
PHP語言的安全問題有其自身語言的一些特點。
14.1 文件包含漏洞
- 文件包含漏洞是“代碼注入”的一種。
- 文件包含可能會出現在JSP、PHP、ASP等語言中。
- 常見的導致文件包含的函數如下:
- PHP: include(), include_once(), require(), require_once(), fopne(), readfile(), …
- JSP/Servlet: ava.io.File(), java.io.FileReader(), …
- ASP: include file, include virtual, …
- 文件包含是PHP的一種常見用法,主要由4個函數完成:
- include()
- require()
- include_once()
- require_once()
- 當使用這4個函數包含一個新的文件時,該文件將作爲PHP代碼執行,PHP內核並不會在意該被包含的文件是什麼類型。
- 這一特性,在實施攻擊時將非常有用。
- 要想成功利用文件包含漏洞,需要滿足下面兩個條件:
- include()等函數通過動態變量的方式引入需要包含的文件
- 用戶能夠控制該動態變量
14.1.1 本地文件包含
- 能夠打開幷包含本地文件的漏洞,被稱爲本地文件包含漏洞(Local File Inclusion,簡稱LFI)
- 字符串截斷的技巧,也是文件包含中最常見的技巧。
- 在連接字符串時,0字節(\x00)將作爲字符串結束符。
- PHP內核是由C語言實現的,因此使用了C語言中的一些字符串處理函數。
- 在一般的Web應用中,0字節用戶其實時不需要使用的,因此完全可以禁用0字節。
- 利用操作系統對目錄最大長度的限制,可以不需要0字節而達到截斷的目的。
- 在連接字符串時,0字節(\x00)將作爲字符串結束符。
- 除了include()等4個函數外,PHP中能夠對文件進行操作的額函數都有可能出現漏洞。能夠讀取敏感文件帶來的後果也是比較嚴重的。
- fopen()
- fread()
- 文件包含漏洞能夠讀取敏感文件或者服務器端腳本的源代碼,從而爲攻擊者實施進一步攻擊奠定基礎。
- 目錄遍歷(Path Traversal)漏洞
- 跨越目錄讀取文件
- 當PHP配置了open_basedir時,將很好地保護服務器,使得這種攻擊無效。
- open_basedir的作用是限制在某個特定目錄下PHP能打開的文件,其作用與safe_mode是否開啓無關。
- 要解決文件包含漏洞,應該儘量避免動態的變量,尤其是用戶可以控制的變量。
- 一種變通方式:使用枚舉。
14.1.2 遠程文件包含
- 如果PHP的配置選項allow_url_include爲ON的話,則include/require函數是可以加載遠程文件的。這種漏洞被稱爲遠程文件包含漏洞(Remote File Inclusion,簡稱RFI)。
- 遠程文件包含漏洞可以直接用來執行任意命令。
14.1.3 本地文件包含的利用技巧
- 本地文件包含漏洞,其實也是有機會執行PHP代碼的,這取決於一些條件。
- 遠程文件包含漏洞之所以能夠執行命令,就是因爲攻擊者能夠自定義被包含的文件內容。因此本地文件包含漏洞想要執行命令,也需要找到一個攻擊者能夠控制內容的本地文件。
- 一些常用的技巧,用於本地文件包含後執行PHP代碼:
- 包含用戶上傳的文件
- 包含data://或php://input等僞協議
- 包含Session文件
- 包含日誌文件,比如Web Server的access log
- 包含/proc/self/environ文件
- 包含上傳的臨時文件(RFC1867)
- 包含其他應用創建的文件,比如數據庫文件、緩存文件、應用日誌等,需要具體文件具體分析。
- 上述技巧的具體討論
14.2 變量覆蓋漏洞
- 安全建議:
- 確保register_globals=OFF。若不能自定義php.ini,則應該在代碼中控制。
- 熟悉可能造成變量覆蓋的函數和方法,檢查用戶能否控制變量的來源。
- 養成初始化變量的好習慣。
14.2.1 全局變量覆蓋
14.2.2 extract()變量覆蓋
14.2.3 遍歷初始化變量
14.2.4 import_request_variable變量覆蓋
14.2.5 parse_str()變量覆蓋
14.3 代碼執行漏洞
- PHP終端代碼執行情況非常靈活,但是依然離不開兩個關鍵條件:
- 用戶能夠控制的函數輸入
- 存在可以執行代碼的危險函數
14.3.1 “危險函數”執行代碼
PHP中,能夠執行代碼的方式遠不止文件包含漏洞一種:
- 比如危險函數popen()、system()、passthru()、exec()等都可以執行系統命令。
- eval()函數可以執行PHP代碼
- 允許用戶上傳PHP代碼
- 應用寫入到服務器的文件內容和文件類型可以由用戶控制
幾個真實案例
14.3.2 “文件寫入”執行代碼
14.3.3 其他執行代碼方式
- 直接執行代碼的函數
- eval()
- assert()
- system()
- exec()
- shell_exec()
- passthru()
- escapeshellcmd()
- pcntl_exec()
- 文件包含
- include()
- include_once()
- require()
- require_once()
- 本地文件寫入
- preg_replace()代碼執行
- 動態函數執行
- Curly Syntax
- 回調函數執行代碼
- unserialize()導致代碼執行
14.4 定製安全的PHP環境
- 熟悉各種PHP漏洞
- 配置php.ini來加固PHP的運行環境
- 推薦php.ini中一些安全相關參數的配置
- register_globals
- open_basedir
- allow_url_include
- display_errors
- log_errors
- magic_quotes_gpc
- cgi.fix_pathinfo
- session.cookie_httponly
- session.cookie_secure
- safe_mode
- disable_functions
- 推薦禁用的類
- XMLWriter
- DOMDocument
- DOMNotation
- DOMXPath
- SQLiteDatabase
- SQLiteResult
- SQLiteUnbuffered
- SQLiteException
- 推薦php.ini中一些安全相關參數的配置
14.5 小結
本章先後介紹了PHP中一些特別的安全問題,比如文件包含漏洞、代碼執行漏洞,最後對如何定製一個安全的PHP環境給出了建議。