周 航 方 勇 黃 誠 劉 亮 陳興剛
1(四川大學電子信息學院 成都 610065)
2(四川大學網絡空間安全學院 成都 610207)
3(成都市計量檢定測試院 成都 610056)
(helloworld@stu.scu.edu.cn)
隨著HTML5的快速發展,Web應用系統在各個行業中的應用越來越廣泛,采用PHP開發的網站更是不計其數.據2014年相關報告統計,Github上PHP開源項目已經接近14萬[1],而這一數據仍在不斷增長.PHP作為腳本語言,存在入門簡單、語法靈活、語法不嚴謹等特點.這些特點導致PHP語言開發的Web系統長期以來遭受網絡攻擊,針對PHP應用的安全研究刻不容緩.
目前針對程序源代碼漏洞檢測方法主要分為2類:動態檢測技術和靜態檢測技術.動態檢測技術可分為主動檢測和被動檢測2種方式:主動檢測利用沙箱環境,將PHP源代碼逐一在沙箱中運行,通過動態分析污點的匯聚點分析漏洞類型;被動檢測主要是對用戶行為進行監控,例如PHP擴展Taint,當用戶輸入的污染數據傳入某些危險函數時擴展就會發出警告[2].靜態分析主要采用了基于數據流分析的污點分析技術,對一個變量而言,它本身只有2種狀態:污染狀態和非污染狀態[3].污點分析可以抽象為一個三元組〈sources,sinks,sanitizers〉,它的整個過程就是分析程序中污點源引入的數據是否不經過無害處理而直接進入污點匯聚點[4].相比于動態檢測,靜態檢測不需要在運行源代碼的情況下進行分析,有著執行速度快、效率高、代碼覆蓋面廣等優點[5].
針對Web二階漏洞檢測技術,國內外的專家學者作了如下研究:Yan等人[6]采用識別準則的方式對二階SQL注入漏洞進行檢測,該方法對準則庫依賴極強并且準則庫需要人為添加維護,在實際環境中不可取;田玉杰等人[7]提出了一種基于改進參數化的二階SQL注入攻擊防御模型,但是該模型的建立是以正常用戶輸入中不允許包含SQL代碼為前提的,誤報率較高且通用性差.Backes等人[8]提出了一種基于語法相似性的方法進行漏洞檢測,該方法針對大規模漏洞檢測非常有效,但是它對模板代碼有著很高的要求,對特定系統的漏洞檢測十分局限.上述文獻中提到的二階漏洞檢測方法普遍存在依賴規則、通用性較差的缺點.針對這些問題,本文主要進行了如下研究及工作:
1)研究了二階漏洞的產生原理以及當前針對此類問題的檢測方法,分析了這些方法在進行二階漏洞檢測時的不足.
2)提出了二階漏洞的靜態檢測方法.在抽象語法樹的基礎上提出了控制流圖的構建方法,利用控制流圖以及語法分析技術重建數據庫讀寫操作模型,結合數據流分析檢測源碼中可能存在的二階漏洞.
3)基于上述方法實現原型系統CodeAn,并選取Github上PHP開源項目進行漏洞檢測.實驗結果表明:該系統誤報率為9%,低于同類漏洞檢測工具.
在常見的一階漏洞中,污點源通常定義為用戶的輸入[9].污點分析的過程就是分析用戶的輸入在到達敏感函數之前是否經過了適當的凈化處理.但是如果污點源引入的數據不直接進入污點匯聚點而是先存入數據庫,當污染的數據再次從數據庫中取出來并且沒有經過適當的凈化處理就進入污點匯聚點,此過程就可能會導致新的Web應用漏洞,這類漏洞通常被定義為二階漏洞.針對此類漏洞的污點分析就可以抽象為一個四元組〈sources,database,sinks,santizers〉.
圖1展示了二階漏洞的模型:攻擊者向服務端發起惡意請求;服務端接收惡意數據后存入數據庫之中;在程序的運行過程中,數據庫中的污染數據被取出來并且沒有進行適當地過濾就直接進入危險函數,導致Web二階漏洞產生.

圖1 二階漏洞模型
本文以存儲型XSS為例(存儲型XSS可以看作是二階XSS漏洞),介紹二階漏洞的產生原理.圖2為1段存儲用戶輸入數據、讀取用戶輸入數據并輸出的代碼.在程序的第2行,服務端獲取用戶的數據并對數據進行SQL注入過濾;第3行將過濾后的數據存入數據庫中;第5行讀取數據庫內容,并且不經過任何過濾直接將內容輸出到頁面.

圖2 二階漏洞代碼示例
在程序執行過程中,系統對數據進行了addslashes過濾防止SQL注入.在系統取出數據之后并沒有進行第2次針對性地過濾,導致XSS跨站腳本漏洞的產生.
用戶需要發送2次請求就可以造成XSS攻擊,如圖3所示:

圖3 用戶攻擊請求
在分析數據庫讀寫操作時,若SELECT語句使用星號查詢所有字段,數據流分析時會無法準確定位數據庫操作的具體列;其次列本身的數據類型和長度可能對數據存在隱式凈化.為了提高數據流分析的準確率和減少不必要的工作量,提出如下方法進行處理.
遍歷源碼中.sql后綴以及.php后綴的文件,利用正則表達式匹配文件中所有CREATE TABLE的SQL語句.使用語法解析器解析并重建數據庫的表結構:包括所有表名、列名、列類型和長度.在MySQL中,字符串之外的數據類型都存在隱式凈化,例如:整數類型Int、時間類型Time等等.針對字符串之外的數據類型,不對該列進行數據庫讀寫分析;而針對字符串類型,設置長度閾值為10.通常長度小于10的列缺乏構造有效攻擊載荷的條件,也不對其進行數據庫讀寫分析.通過處理,得到表1所示的數據庫結構信息:

表1 數據庫結構信息
二階漏洞模型如圖1所示,由圖1可知漏洞產生的根本原因是數據庫中的污染數據傳入了敏感函數.本文將PHP中的100多個敏感函數根據漏洞類型分為8類,部分信息如表2所示:

表2 敏感函數及分類
為了分析數據在databases→sinks之間的傳播,本文進行了如下研究:
在控制流圖中,函數調用的語句可以抽象為Expr-FuncCall〈name,args,result〉,其中name表示函數名,args表示參數,result表示函數返回值;數組元素的訪問可以抽象為Expr-ArrayDim-Fetch〈var,dim,result〉,其中var表示數組名,dim表示數組鍵名,result表示返回值.
PHP中使用內置的query系列函數獲取數據庫資源標識符,為了構建數據庫的讀操作模型,利用控制流圖獲取query函數的參數args,此參數為待執行的SQL語句.通過語法解析器分析數據庫讀操作查詢的相關表名以及字段名.如果存在UNION或者JOIN操作,則可能存在多個表;如果存在別名操作,則需要通過數據庫結構信息進行別名映射.PHP中使用內置的fetch系列函數將數據庫資源標識符轉換為關聯數組或索引數組.在對數據庫查詢結果數組進行訪問時,若Expr-ArrayDimFetch中的屬性dim為字符串類型,則其值與數據庫中的列名一致;若屬性dim為數字類型,則通過數據庫結構信息查找相應的列名.
遍歷控制流圖中的基本塊,獲取基本塊中所有函數調用語句Expr-FuncCall及其屬性name和args.根據表2提供的列表,提取敏感函數調用的相關信息.以敏感函數所在的控制流圖基本塊為起點進行后向數據流分析,對敏感函數的參數args構建數據依賴關系,標記敏感參數args到數據庫資源標識符的所有有效路徑.
從二階漏洞模型可知,漏洞產生的另外一個原因是用戶輸入的污染數據sources存儲進數據庫之中.sources點規則定義如表3所示:

表3 sources列表
為了分析數據在sources→databases之間的傳播,本文進行了如下研究:
MySQL中存在3種數據庫寫操作語句:INSERT,REPLACE,UPDATE.INSERT和REPLACE用于向數據庫中插入數據,UPDATE用于向數據庫中更新數據.此處對UPDATE語句進行分析,其語法結構如下所示:

在構建數據庫寫操作模型時,主要針對表、列以及列輸入變量進行分析.UPDATE語法中的assignment的結構為col-name=value,通常列輸入變量為value.而語法結構中的WHERE子句用于界定數據更新的條件,LIMIT用于界定可更新的行數.本文只關注外部數據到數據庫之間的傳遞,對限定語句不進行研究.
遍歷控制流圖中的基本塊,獲取數據庫語句執行函數query的參數args,此參數為待執行的SQL語句.通過語法解析器進行解析[10]提取數據庫寫操作的表、列以及輸入變量,結合數據庫結構得到表4所示的數據庫寫操作模型.而針對存在隱式凈化的列字段,不對其相應變量進行下一步的數據依賴關系分析.

表4 數據庫寫操作模型
以列的輸入變量所在控制流圖基本塊為起點進行后向數據流分析,對輸入變量構建數據依賴關系.在依賴關系中,依賴變量來自表3中的sources列表,則標記一條有效路徑并結束當前變量的分析,直到所有變量的路徑標記完畢.
控制流圖[11](control flow graph,CFG)也稱控制流程圖,由圖形符號表示程序在執行期間可能通過的所有執行路徑.控制流圖可通過遍歷AST節點,利用控制節點分割基本塊得到.PHP中控制節點及其分類如圖4所示,構建方法如過程1所示.

圖4 用戶攻擊請求
過程1.CFG生成過程.
1)遍歷AST節點;
2)如果節點屬于STMT-Jump,即為每個分支建立新的基本塊,并且將新的基本塊用有向邊連接到前一個基本塊,跳轉條件被添加到有向邊上;
3)如果節點屬于STMT-Loop,即建立一個新的基本塊,在數據流分析期間對循環條件進行分析;
4)如果節點屬于STMT-Stop或STMT-Return,則停止解析;
5)一旦創建新的基本塊,即對基本塊進行數據流模擬并創建塊摘要;
6)所有AST節點遍歷結束之后停止本過程.
數據流分析以控制流圖中的基本塊為最小單位,在控制流圖的構建過程中模擬每個基本塊的數據流.
最常見的數據依賴[12]情況就是賦值運算.如果變量$x的取值決定于$y,那么就可以認為$x依賴于$y.在分析的過程中,對每個基本塊的變量進行搜集,構建數據依賴關系.
但是PHP中允許使用多個變量指向同一個內容[13],這種關系稱為引用.如果存在引用關系,則可能導致分析存在缺陷.因此在基本塊的數據流模擬時,將所有引用關系指向保存的一個引用結構中.
數據流分析的關鍵就在于為每個數據匯聚點構建數據依賴圖,由于當前塊中數據只依賴于之前基本塊中的數據[14],在控制流圖的基礎上使用后向的數據流分析方法:循環遍歷當前基本塊的所有入口邊緣,并在每個數據塊摘要的屬性中查找污點變量的名稱.如果找到匹配項,則通過依賴分析以及引用分析將變量替換為對應的依賴變量,并復制所有污點標記以及凈化標記.最后通過鏈接到基本塊的所有入口邊緣繼續跟蹤,直到標記出所有的路徑.數據依賴圖示例如圖5所示:

圖5 數據依賴圖
在數據流分析的過程中,內置函數對數據的處理會改變其原有的污點屬性[15].本文把內置的凈化函數分為4類:編解碼類、安全類、散列類、強制轉換類.
編解碼類:例如base64-decode,base64-encode等,在進行污點分析的過程中,數據經過編碼函數處理之后則去除污點標記,經過解碼函數處理之后恢復原有污點標記.
安全類:例如addslashes,stripslashes等,在數據流的分析過程中,數據經過前者處理之后去除對應的污點標記,經過后者處理之后恢復對應的污點標記.
散列類:例如md5等,數據經過此類函數處理之后,污染源的所有污點屬性被清除.
強制轉換類:例如intval等,與散列類函數一樣,去除所有污點屬性.
本文提出了一種針對PHP源代碼Web二階漏洞的檢測模型,系統整體框架如圖6所示.系統第1部分使用語法解析器對PHP源碼進行預處理,讀取源碼并轉換為token序列,然后通過語法規則將token序列轉換為AST.第2部分遍歷抽象語法樹節點,并利用上文提到的方法構建程序控制流圖:利用控制節點將源碼分割成單獨的基本塊,并在基本塊中進行數據流模擬,并提取基本塊的塊摘要.第3部分以數據庫為中心,對其讀寫操作進行建模:利用正則表達式提取PHP應用中創建表的SQL語句,通過語法分析構建數據庫結構信息.在抽象語法樹和控制流圖的基礎上重構SQL執行語句,對數據庫讀寫操作模型進行重建.對于數據庫的讀寫分析分為2部分:databases→sinks以及sources→databases之間的數據流傳播.本文利用后向數據流分析技術對敏感變量構建數據依賴圖,標記所有的有效路徑.在敏感函數和內置凈化函數的基礎上分析可能存在的二階漏洞.
為了驗證理論的可行性及有效性,本文實現了原型系統CodeAn.選擇了Github上10個PHP開源項目進行性能分析.實驗環境如表5所示,測試采用了誤報率以及總耗時2個關鍵要素進行評估.實驗步驟如下:

圖6 系統框架圖
1)選取Github上10個PHP開源項目,分別選取某一版本作為檢測對象并統計其公開的二階漏洞.
2)搭建原型系統CodeAn,分別使用常見漏洞檢測工具Fortify,RIPS,CodeAn對PHP開源項目進行漏洞檢測.
3)從檢測漏洞數、總耗時、誤報率3個方面對檢測結果進行統計,得到實驗結果.
實驗結果如表5所示,RIPS檢測漏洞數23,誤報率為17%,總耗時3 733 s;Fortify檢測漏洞數31,誤報率為23%,總耗時4 033 s;CodeAn檢測漏洞數28,誤報率為9%,總耗時2550s.通過分析發現,Fortify和RIPS產生的誤報事件都集中在2個方面:一方面是未考慮數據庫中字段類型以及長度對數據產生的隱式凈化;另一方面是在數據流分析的過程中,沒有考慮解碼函數以及散列函數對數據產生的影響,而前者會恢復數據的污點標記,后者會去除數據的污點標記.
相比Fortify和RIPS而言,本系統有著更低誤報率和更快的響應時間.

表5 實驗結果
本文闡述了二階漏洞的產生原理并提出了針對此類漏洞的檢測方法:利用控制流圖和語法分析技術對數據庫讀寫操作進行建模,以數據庫為中心進行數據流分析.實驗結果表明,文中提到的二階漏洞檢測方法和常見工具相比,誤報率更低,耗時更短.
本文主要針對面向過程的PHP應用程序,針對面向對象的PHP應用程序檢測效果不佳.下階段將針對此方向進行更深入的研究.
[1]Zapponi C.Programming languages and Git Hub[J/OL].[2017-11-10].http://githut.info/
[2]Nguyen-Tuong A,Guarnieri S,Greene D,et al.Automatically hardening Web applications using precise tainting[C]//Porc of Int Federation for Information Processing.Berlin:Springer,2005:295-307
[3]霍志鵬.基于靜態分析的PHP代碼缺陷檢測[D].北京:北京郵電大學,2015
[4]王蕾,李豐,李煉,等.污點分析技術的原理和實踐應用[J].軟件學報,2017,28(4):860-882
[5]王耀輝,王丹,付利華.面向PHP程序的SQL漏洞檢測系統[J].計算機工程,2016,42(4):112-118
[6]Yan L,Li X,Feng R,et al.Detection method of the second-order SQL injection in Web applications[G]//LNCS 8332:Proc of Int Workshop on Structured Object-Oriented Formal Language and Method.Berlin:Springer,2013:154-165
[7]田玉杰,趙澤茂,張海川,等.二階SQL注入攻擊防御模型[J].信息網絡安全,2014(11):70-73
[8]Backes M,Rieck K,Skoruppa M,et al.Efficient and flexible discovery of PHP application vulnerabilities[C]//Proc of IEEE European Symp on Security and Privacy.Piscataway,NJ:IEEE,2017:334-349
[9]Papagiannis I,Migliavacca M,Pietzuch P.PHP aspis:Using partial taint tracking to protect against injection attacks[C]//Proc of USENIX Conf on Web Application Development.Berkeley:USENIX Association,2011:2- 2
[10]Cao D,Bai D.Design and implementation for SQL parser based on ANTLR[C]//Proc of Int Conf on Computer Engineering and Technology.Piscataway,NJ:IEEE,2010:V4-276-V4-279
[11]夏玉輝,張威,萬琳,等.一種基于控制流圖的靜態測試方法[C]//全國軟件測試會議與移動計算、柵格、智能化高級論壇會議錄.武漢:中國計算機學會容錯計算專業委員會,2009
[12]聶世超.PHP程序靜態分析系統的設計與實現[D].長春:吉林大學,2011
[13]The PHP Group.References explained[EB/OL].[2017-11-10].http://php.net/manual/zh/language.references.php
[14]Xie Y,Aiken A.Static detection of security vulnerabilities in scripting languages[C]//Proc of USENIX Security Symp.Berkeley:USENIX Association,2006:179-192
[15]Ouni A,Kessentini M,Inoue K,et al.Search-based Web service antipatterns detection[J].IEEE Trans on Services Computing,2015,10(4):603-617