代碼審計(code audit)是一種通過分析源代碼來發現程序錯誤、安全漏洞和違反程序規范的技術。它屬于防御性編程的一部分,旨在減少程序發布前的錯誤。
C和C++源代碼是最常見的審計對象,因為其他高級語言如python,其底層實現依賴于c語言,經過處理后被Python封裝,易受攻擊的功能較少(例如,Python幾乎不需要考慮邊界檢查函數的漏洞)。
代碼審計的對象包括但不限于對windows和linux系統下的以下語言進行審核:Java,C,C#,ASP,php,jsp,.NET。
本系列代碼審計文章主要針對PHP語言展開,第一課首先為大家介紹一些PHP基礎知識。
PHP網頁工作原理
立即學習“PHP免費學習筆記(深入)”;
要學習PHP代碼審計,首先需要了解PHP網頁的組成部分。
- 語言網頁類型:網頁中有動態語言和靜態語言,具體區別如下表所示。
動態語言 | 靜態語言 |
---|---|
PHP語言 | html,文本文件,圖片文件 |
需要專門的解釋器才能被服務器識別 | 可以被服務器(如nginx、apache等Web服務器)直接識別 |
對每個客戶端的返回可能不一樣 | 返回固定 |
因此,引入了FastCGI,實現了master進程和worker進程常駐,優化了效率。
Web服務器和CGI接口通過一些環境變量向CGI程序傳遞重要參數。以下是一些常用的CGI環境變量:
變量名 | 描述 |
---|---|
CONTENT_TYPE | 指示所傳遞信息的MIME類型。通常為application/x-www-form-urlencoded,表示數據來自HTML表單。 |
CONTENT_LENGTH | 如果服務器與CGI程序信息的傳遞方式是POST,這個環境變量的值表示從標準輸入STDIN中可讀的有效數據的字節數。在讀取輸入數據時必須使用。 |
http_Cookie | 客戶機內的COOKIE內容。 |
HTTP_USER_AGENT | 提供包含版本號或其他專有數據的客戶瀏覽器信息。 |
PATH_INFO | 表示緊接在CGI程序名之后的其他路徑信息,常作為CGI程序的參數出現。 |
QUERY_STRING | 如果服務器與CGI程序信息的傳遞方式是GET,這個環境變量的值表示所傳遞的信息,信息跟在CGI程序名之后,兩者中間用一個問號’?’分隔。 |
REMOTE_ADDR | 發送請求的客戶機的IP地址。例如上面的192.168.1.67。這個值總是存在的,是Web客戶機需要提供給Web服務器的唯一標識,可用于區分不同的Web客戶機。 |
REMOTE_HOST | 包含發送CGI請求的客戶機的主機名。如果不支持查詢,則無需定義此環境變量。 |
REQUEST_METHOD | 提供腳本被調用的方法。對于使用HTTP/1.0協議的腳本,僅GET和POST有意義。 |
SCRIPT_FILENAME | CGI腳本的完整路徑 |
SCRIPT_NAME | CGI腳本的名稱 |
SERVER_NAME | 你的WEB服務器的主機名、別名或IP地址。 |
SERVER_SOFTWARE | 包含了調用CGI程序的HTTP服務器的名稱和版本號。例如,上面的值為Apache/2.2.14(unix) |
- 服務器:不同服務器的特點、優點和缺點如下表所示。
Web Server | 特點 | 優點 | 缺點 |
---|---|---|---|
Nginx | 基于事件驅動 | 性能、負載均衡,穩定性高,支持熱部署 | / |
Apache | 基于進程驅動 | 支持幾乎所有平臺,組件多 | 系統壓力大,不支持熱部署 |
iis | 最適合ASP.NET、ASP | 產品相較成熟 | 只能運行在Windows平臺 |
PHP核心配置
代碼在不同環境下執行結果會有不同,不同版本會導致指令的變更,因此代碼審計需要熟悉各個版本配置文件的核心指令,以達到以下兩個基本目的:
- 擴展審計過程中的攻擊面(訪問目錄,訪問輸出內容,數據過濾,文件處理范圍,函數調用等)。
- 方便審計過程中調試和信息的輸出(display_errors, error_reporting控制錯誤信息顯示)。
兩個主要的PHP配置文件:
- php.ini:PHP配置中的幾種模式及其含義如下:
模式 | 含義 |
---|---|
PHP_INI_USER | 可在用戶腳本(例如ini_set())或Windows注冊表以及.user.ini中設定 |
PHP_INI_PERDIR | 可在php.ini, .htAccess或httpd.conf設定 |
PHP_INI_SYSTEM | 可在php.ini或https.conf中設定 |
PHP_INI_ALL | 可在任何地方設定 |
- .user.ini:用戶自定義的小型php.ini,會影響到PHP_INI_USER, PHP_INI_PERDIR模式下的配置。除了主php.ini之外,PHP還會在每個目錄下掃描.user.ini文件,且.user.ini能被動態加載,修改后不需要重啟服務器。
注意,PHP的核心配置項不一定是在php.ini中設置的。
審計中常涉及的配置:
-
register_globals(在PHP5.4.0被移除):隱患是直接將用戶通過GET、POST提交的參數注冊為全局變量,并初始化值為參數對應的值,使參數可以在腳本中直接使用。
-
magic_quote_gpc(PHP 5.4后被取消):開啟后會對GET、POST、Cookie變量中的單引號、雙引號、反斜杠以及空字符(NULL)前面加上反斜杠。隱患是若在代碼層再次對單引號等進行特殊轉換過濾,可能導致過濾失效。
-
magic_quotes_runtime:與magic_quotes_gpc相同,是在單引號、雙引號、反斜杠以及空字符前面加反斜杠,區別是magic_quotes_runtime是對從數據庫或文件中獲取的數據進行過濾。隱患是當該項開啟時,若在代碼層再次對單引號等進行特殊轉換過濾,可能導致過濾失敗。
-
magic_quotes_sybase(PHP5.4后被取消):當magic_quotes_gpc也開啟時,會覆蓋掉magic_quotes_gpc的配置,同樣對GET、POST、Cookie進行處理,但僅僅是把轉移空字符和單引號變成雙單引號(用單引號來轉義單引號)。
- Magic_quotes_sybase=0時,addslashes將對” ‘ 進行轉義操作;
- Magic_quotes_sybase=1時,addslashes將對’進行”轉義操作。
隱患是當該項與magic_quotes_gpc同時開啟時,可能導致參數意外閉合。
實例如下:
注意,%27經URL解碼后為’
-
safe_mode(PHP5.3以上的版本棄用):PHP的安全模式是一個非常重要的內嵌安全機制,能夠控制一些PHP中的函數是否能夠正常使用,比如system(),同時對很多文件操作函數進行權限控制,也不允許對某些關鍵文件進行操作,比如/etc/passwd,除非使用safe_mode_include_dir和safe_mode_exec_dir指定一個可被包含和存放了外部腳本的安全目錄。
隱患是當該項開啟時,如果本身敏感操作的拼接為雙引號,可能導致參數意外閉合。
-
allow_url_include、allow_url_fopen:使include()、include_once()、require()、require_once()都可以引入URL類型協議,允許其包含遠程文件。
- allow_url_fopen:是否允許將URL(如http://或ftp://)作為文件處理。
- allow_url_include:是否允許include/require打開URL(如http://或ftp://)作為文件處理。
隱患是從PHP5.2開始allow_url_include就默認為Off,而allow_url_fopen一直是On的。在文件包含漏洞中,會產生遠程文件包含的動作,增加攻擊面。
-
open_basedir:open_basedir是PHP設置中為了防御PHP跨目錄進行文件(目錄)讀寫的方法,所有PHP中有關文件讀、寫的函數都會經過open_basedir的檢查。將PHP所有能打開的文件限制在指定的目錄樹,包括文件本身。
本指令不受安全模式打開或關閉的影響。
隱患是雖然在PHP5.3以后很少有能夠繞過open_basedir讀寫文件的方法,但是有很多可以繞過open_basedir的限制對其進行輸出目錄的繞過方法。
-
disable_functions:可以禁止一些敏感函數的使用。
一個目前推薦的禁用函數列表有:
函數 作用 dl() 載入指定參數的PHP擴展 exec() 執行一個外部程序 system() 執行外部程序,并且顯示輸出 passthru() 同exec()函數類似,passthru()函數也是用來執行外部命令的 proc_open() 執行一個命令,并且打開用來輸入/輸出的文件指針 pcntl_exec() 在當前進程空間執行指定程序,可指定參數 shell_exec() 通過shell環境執行命令,并且將完整的輸出以字符串的方式返回 mail() 發送郵件 imap_open() 打開IMAP流,參數username為用戶帳號。參數password為用戶的密碼 imap_mail() 通過IMAP發送郵件 putenv() 添加setting到服務器環境變量。環境變量僅存活于當前請求期間。在請求結束時環境會恢復到初始狀態 ini_set() 設置指定配置選項的值。這個選項會在腳本運行時保持新的值,并在腳本結束時恢復 apache_setenv() 設置Apache子進程環境變量 symlink() 建立符號連接 link() 建立一個硬連接 -
display_errors、error_reporting:在調試PHP時,打開錯誤信息,設置錯誤顯示級別。
隱患是敏感信息泄露。在審計中,開啟錯誤信息顯示可以更加直觀地了解數據產生的錯誤提示,會暴露目錄、絕對路徑,造成sql注入的錯誤信息。
-
auto_append_file、auto_prepend_file:自動在文件的尾或頭處通過require引入特定的文件。
隱患是通過上傳.user.ini可能造成上傳白名單擴展名的文件也能getshell。