文獻(xiàn)標(biāo)識碼: A
文章編號: 0258-7998(2013)04-0137-04
SQL注入攻擊是一種常見的互聯(lián)網(wǎng)攻擊手段,,它具有易操作,、強(qiáng)隱蔽、高危害和難檢測等特點(diǎn),。由于SQL注入攻擊的對象是Web服務(wù)器后臺的數(shù)據(jù)庫,,而數(shù)據(jù)庫往往存放重要的用戶數(shù)據(jù)或業(yè)務(wù)數(shù)據(jù),因而SQL注入攻擊的后果影響非常嚴(yán)重,。特別對銀行,、證券、電信,、移動,、政府以及電子商務(wù)企業(yè)等后臺數(shù)據(jù)庫,。一旦遭受SQL注入而導(dǎo)致數(shù)據(jù)纂改或機(jī)密數(shù)據(jù)丟失,在經(jīng)濟(jì)角度和社會角度都將產(chǎn)生惡劣的影響,。因此檢測和防范SQL注入是軟件安全領(lǐng)域的重要課題,,具有極高的研究價值和意義。
常見的Web應(yīng)用系統(tǒng)架構(gòu)如圖1所示,其中包括Web服務(wù)器及后臺數(shù)據(jù)庫,。在Web服務(wù)器上部署Web服務(wù),,用戶使用瀏覽器通過Http或Https協(xié)議與Web服務(wù)器進(jìn)行交互。常稱之為B/S架構(gòu),。
在該架構(gòu)中,,Web服務(wù)器向用戶提供各種應(yīng)用服務(wù),這些服務(wù)都涉及到與數(shù)據(jù)庫的交互,,即伴執(zhí)行SQL語句,。如果Web應(yīng)用系統(tǒng)設(shè)計(jì)或?qū)崿F(xiàn)不合理,將可能導(dǎo)致用戶對數(shù)據(jù)庫執(zhí)行的SQL語句偏離系統(tǒng)設(shè)計(jì)的預(yù)期,從而引發(fā)安全事故,。
Java程序運(yùn)行在JVM中,,因?yàn)榫哂信c平臺無關(guān)的特點(diǎn),這種完全面向?qū)ο箝_發(fā)的語言廣泛應(yīng)用在Web系統(tǒng)的設(shè)計(jì)中,。針對Java源代碼的SQL注入攻擊檢測進(jìn)行研究,,對于提高Web應(yīng)用系統(tǒng)的安全性具有重要意義。
本文介紹了SQL注入原理,、目前的檢測方法以及Java代碼對數(shù)據(jù)庫操作的三種代碼結(jié)構(gòu),。針對Java執(zhí)行SQL語句的特點(diǎn),重點(diǎn)提出一種基于抽象語法樹進(jìn)行SQL執(zhí)行路徑檢測的SQL注入檢測算法,;在Web 應(yīng)用程序發(fā)布前進(jìn)行深入靜態(tài)分析和檢查,,從而提高Web應(yīng)用系統(tǒng)的安全性。算例分析和在實(shí)際Web系統(tǒng)中的應(yīng)用結(jié)果表明,,本文提出的SQL注入檢測算法具有高檢測率和低誤報率,。
1 相關(guān)研究工作
SQL注入漏洞最早在2000年左右被提出,其后學(xué)者們對SQL注入的檢測和防護(hù)展開了一系列的研究,。
典型的一種SQL注入例子如下:
(1)用戶登錄Web應(yīng)用系統(tǒng)時,,需要進(jìn)行身份認(rèn)證,主要輸入用戶名和密碼兩個變量v_usr和v_pwd,;
(2)Web系統(tǒng)執(zhí)行合法性檢查的SQL語句為:“Select * from users where username=‘”+v_usr+“’and password=‘”+ v_pwd+“’”,如果用戶登錄時用戶取為‘admin’ or 1=1--,,那么合法性檢查的SQL語句等效于Select * from users where username=‘ admin’ or 1=1;
顯然,用戶名取‘admin’ or 1=1--時,,無論密碼輸入多少,,都可以登錄系統(tǒng)。
SQL注入的檢測和防護(hù)方式目前主要有兩大類,,一是系統(tǒng)上線前檢測,,也稱為靜態(tài)檢測,;二是系統(tǒng)在線運(yùn)行防御,也稱為動態(tài)檢測,。
動態(tài)檢測方式是一種黑盒檢測方法,,對上線的系統(tǒng)進(jìn)行SQL漏洞掃描,編制SQL注入攻擊腳本對Web系統(tǒng)進(jìn)行試探,,通過檢查Http的回應(yīng)報文內(nèi)容來判斷是否發(fā)生SQL注入攻擊,,從而確定是否存在SQL注入漏洞。AppScan等工具可執(zhí)行此類安全檢測,。
靜態(tài)檢測方法是一種白盒檢測方法[1],,通過靜態(tài)語法解析查找Web 應(yīng)用代碼中可能引發(fā)SQL注入的環(huán)節(jié),在Web應(yīng)用發(fā)布前檢查代碼質(zhì)量,。Fotify等工具可執(zhí)行此類安全檢測,。參考文獻(xiàn)[2-3]給出一種動態(tài)生成SQL語句進(jìn)行類型正確性檢查的方法來檢測是否存在SQL注入,該方法的的缺點(diǎn)在于只能檢測句法結(jié)構(gòu)或語句類型出現(xiàn)異常的SQL注入問題,。參考文獻(xiàn)[4]提出一種自動推理機(jī)的方法對添加了輸入值后的SQL語句進(jìn)行檢查,,該方法的缺點(diǎn)在于只能檢測出重言式的SQL注入攻擊。此外,,還有一些學(xué)者采用機(jī)器學(xué)習(xí)算法對SQL注入漏洞進(jìn)行檢測[5],,這一類檢測方法的檢測率和誤報率往往依賴于訓(xùn)練集的大小。
2 Java源代碼靜態(tài)掃描分析思路
程序靜態(tài)分析指在不執(zhí)行程序的情況下,,通過自動掃描代碼發(fā)現(xiàn)隱含的程序隱患[6],,具備執(zhí)行速度快、效率高等優(yōu)點(diǎn),。對源代碼進(jìn)行靜態(tài)分析時借鑒編譯技術(shù)的詞法分析和語法分析,。源代碼進(jìn)行靜態(tài)分析掃描原理如圖2所示。
Java程序與數(shù)據(jù)庫之間的交互主要通過Java數(shù)據(jù)庫連接JDBC進(jìn)行,。其數(shù)據(jù)庫操作涉及與數(shù)據(jù)庫建立連接,、發(fā)送SQL指令、處理返回結(jié)果,、斷開數(shù)據(jù)庫連接4個步驟,。
具體實(shí)現(xiàn)上主要涉及的Java類有Connection類、DriverManager類,、Statement類,、ResultSet類、PreparedStatement類和callableStatement類,。在代碼實(shí)現(xiàn)上往往有3種實(shí)現(xiàn)方式:
(1) 常規(guī)方式
//函數(shù) DB_SQL(SQL語句)
Connection con=DriverManager. getConnection(參數(shù)URL,,參數(shù)用戶名,參數(shù)密碼);
Statement stat = con.createStatement();
ResultSet rs = stat.executeQuery("SQL語句");(或者executeUpdate /execute /executeBatch)
//此處為rs的處理代碼
rs.close(); stat.close(); con.close();
Java采用常規(guī)方式訪問數(shù)據(jù)庫,,每次都需要進(jìn)行建立連接,,再執(zhí)行SQL語句,,最后釋放連接。由于建立連接過程一般比較耗時(一般需要幾十毫秒至幾百毫秒之間),,而且Statement對象每次執(zhí)行SQL語句的解析和編譯也比較耗時,,如果涉及如下循環(huán)使用情況,則代碼的執(zhí)行效率將會比較低下,。為此,,對于需要頻繁建立數(shù)據(jù)庫連接的情況,可以采用連接池方式進(jìn)行優(yōu)化,;對于需要頻繁執(zhí)行相似SQL語句的情況,,則可采用預(yù)編譯方式進(jìn)行優(yōu)化。
while(N次)
{
DB_SQL(SQL語句);
}
(2) 預(yù)編譯方式
采用常規(guī)方式執(zhí)行SQL語句時,,DBMS需要對SQL語句進(jìn)行解析和編譯,;而采用預(yù)編譯方式時,DBMS只在第一次對SQL語句進(jìn)行解析和編譯,。相比前者,,后者更難注入SQL。原因在于,,PreparedStatement對象執(zhí)行SQL語句前,需要設(shè)置“?”號對應(yīng)的變量,,例如ps.setInt(1,變量v1),、ps.setLong(2,變量v2) 表示SQL語句中第一個“?”號是int型、值為“變量v1”,;這樣在安全性上相當(dāng)于增加了一個類型匹配,。
Connection con=DriverManager.getConnection(參數(shù)URL,參數(shù)用戶名,,參數(shù)密碼);
while(N次)
{
PreparedStatement ps = con.prepareStatement(帶若干“?”號的SQL語句);
ps.setXX(“,?”的序號, 變量); //XX包括int double等
ResultSet rs = ps.executeQuery();(或者executeUpdate /execute /executeBatch)
//此處為rs的處理代碼
rs.close();
}
ps.close(); con.close();
(3) 連接池方式
連接池(Connection Pool)是設(shè)計(jì)模式中資源池(Resource Pool)的一種具體應(yīng)用,主要解決資源頻繁分配和釋放所帶來的性能問題,。其基本思想是預(yù)先建立一個數(shù)據(jù)庫連接池,,在該連接池中預(yù)先存放一定數(shù)量的連接,當(dāng)需要建立數(shù)據(jù)庫連接時,,并非新建一個數(shù)據(jù)庫連接,,而是從連接池中取出一個連接對象,然后對該連接對象進(jìn)行參數(shù)設(shè)置和使用,,使用完畢后,,并不釋放該連接,而是將連接對象的參數(shù)復(fù)位并回收,。
采用連接池的應(yīng)用開發(fā)方式與常規(guī)方式的代碼結(jié)構(gòu)類似,。區(qū)別是常規(guī)方式的連接對象在需要時才從堆中動態(tài)分配空間與數(shù)據(jù)庫建立連接,,使用完畢后即斷開連接,等待Java的GC垃圾回收器發(fā)現(xiàn)該對象不再需要后,將回收連接對象的內(nèi)存空間,;而采用連接池方式,則是在連接池中預(yù)先建立一個若干數(shù)據(jù)的連接對象,,需要使用時才從連接池中取出一個連接對象(不是臨時創(chuàng)建,因此效率高),,使用完畢后,,并不斷開連接,而是將該連接對象歸還給對象池,。通過一個“借用”和“歸還”的方式使得可以高效地對付頻繁的數(shù)據(jù)庫訪問操作,。目前常用的連接池主要有C3PO、BoneCP,、DBCP,、Proxool等。
綜上所述,無論采用哪一種數(shù)據(jù)庫操作方式,,Java源代碼中可能出現(xiàn)SQL注入漏洞的代碼執(zhí)行過程中,,一定經(jīng)過executeQuery、executeUpdate,、execute 或executeBatch函數(shù),。本文提出的SQL檢測算法,正是通過定位這些函數(shù)并以此為分析SQL注入隱患的切入點(diǎn),,跟蹤這些函數(shù)的參數(shù)或參數(shù)表達(dá)式,。如果這些參數(shù)表達(dá)式最終能追溯到某個特定的對象(如HttpServletRequest),則可以肯定這條路徑將可能存在SQL注入隱患,。
3 Java源代碼SQL注入檢測算法
抽象語法樹AST(Abstract Syntax Tree)是程序源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式,,樹上的每個節(jié)點(diǎn)表示源代碼中的一種結(jié)構(gòu),AST的好處在于不依賴于具體的方法和語言細(xì)節(jié),。對于源代碼的文法分析,,首先進(jìn)行詞法分析,將源代碼中所有字符串從前至后逐個字符進(jìn)行掃描,,并對每個“單詞”進(jìn)行標(biāo)識,。這些“單詞”主要包括Java語言中的關(guān)鍵字、標(biāo)識符,、運(yùn)算符和分隔符等,。然后進(jìn)行語法分析,將這些識別出來的“單詞”序列分解成各類Java語法單位,。
根據(jù)源代碼靜態(tài)分析掃描原理,,結(jié)合Java 1.6的文法定義,本文提出的SQL注入檢測算法通過對源代碼的詞法和語法分析,,生成相應(yīng)的抽象語法樹,,定義規(guī)則,,根據(jù)規(guī)則遍歷抽象語法樹。
本文提出的檢測算法實(shí)現(xiàn)步驟如下:
(1) 遍歷抽象語法樹,尋找METHOD_DECL結(jié)點(diǎn)中節(jié)點(diǎn)名為executeQuery或executeUpdate或execute或executeBatch的所有節(jié)點(diǎn),并將它們保存在哈希表keyNode中,。
(2) 找keyNode中的每一個節(jié)點(diǎn),,確認(rèn)是否是正確的結(jié)點(diǎn)。首先得到keyNode節(jié)點(diǎn)的前繼表達(dá)式,,并得到前繼表達(dá)式的返回值類型,。若前繼表達(dá)式的返回值類型為Java.sql.Statement,則可確認(rèn)此結(jié)點(diǎn),,并轉(zhuǎn)到下一步,;若返回值類型不是Java.sql.Statement,則轉(zhuǎn)步驟(2),,檢測下一個節(jié)點(diǎn),;
(3) 確認(rèn)結(jié)點(diǎn),取得第一個參數(shù)作為路徑結(jié)點(diǎn),,分析并跟蹤路徑結(jié)點(diǎn)的數(shù)據(jù)流,。具體為:分析并跟蹤路徑結(jié)點(diǎn)表達(dá)式為變量表達(dá)式;分析并跟蹤路徑結(jié)點(diǎn)表達(dá)式為方法表達(dá)式,;跟蹤退出的準(zhǔn)則描述如下:①當(dāng)所有跟蹤變量均到達(dá)常量定值,,則此路徑不會產(chǎn)生SQL注入漏洞,轉(zhuǎn)步驟(2),;②跟蹤到開始API定義的方法結(jié)點(diǎn),,轉(zhuǎn)步驟(4)。
(4) 記錄跟蹤到的SQL注入的侵入路徑,,轉(zhuǎn)步驟(2),檢測下一個節(jié)點(diǎn),。若keyNode中所有結(jié)點(diǎn)都已檢測完成,,則算法終止。
4 算法實(shí)驗(yàn)
為了驗(yàn)證本文提出的算法的有效性,,以一個Java程序?yàn)閷?shí)驗(yàn)算例進(jìn)行分析和說明,,實(shí)驗(yàn)算例代碼如圖3所示。
在實(shí)驗(yàn)的Java源代碼中,,可能產(chǎn)生SQL注入的開始API和結(jié)束API(即Java源代碼SQL注入檢測規(guī)則),,具體如表1所示。
根據(jù)SQL注入檢測規(guī)則,,利用本文所提出的檢測算法對圖3所示的Java源代碼進(jìn)行掃描分析,,產(chǎn)生的抽象語法樹如圖4所示,可發(fā)現(xiàn)SQL注入漏洞,,并報告相應(yīng)的路徑,。
本文提出的SQL注入檢測算法已經(jīng)成功應(yīng)用于本單位信息系統(tǒng)的Java源代碼靜態(tài)檢測中,。在新上線運(yùn)行的某Web系統(tǒng)(約10萬行代碼)靜態(tài)分析中,檢測時間僅為5 s,,檢測準(zhǔn)確識別率高達(dá)90%,,而誤報率僅有10%;通過實(shí)驗(yàn)和實(shí)際系統(tǒng)的測試和驗(yàn)證,,本文提出的Java源代碼SQL注入靜態(tài)分析算法具有很好的應(yīng)用效果和前景,。
參考文獻(xiàn)
[1] WILLIAM G J, VIEGAS H J, ORSO A. A classification of SQL injection attacks and countermeasures[C]. Proc. of International Symposium on Secure Software Engineering.2006.
[2] GOULD C, SU Z, DEVANBU P. JDBC checker: a static analysis tool for SQL/JDBC applications[C]. Proceeding of the 26th International conference on Software Engineering(ICSE). Washington D C: IEEE computer Society, 2004.
[3] GOULD C, SU Z, DEVANBU P. Static checking of dynamically generated queries in database applications[C].Proceedings of 26th International Conference on Software Engineering, 2004.
[4] WASSERMANN G, SU Z. An analysis framework for security in Web applications[C]. Proceedings of the FSE Work shop on Specification and Verification of Component Based System, 2004.
[5] HUANG Y W, HUANG S K, LIN T P, et al. Web application security assessment by fault injection and behavior monitoring[C]. Porceeding of the 11th International World Wide Conferecne, 2002.
[6] 張卓.SQL注入攻擊技術(shù)及防范措施研究[D].上海:上海交通大學(xué),2007.