文獻(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)域的重要課題,具有極高的研究價(jià)值和意義。
常見的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注入檢測算法具有高檢測率和低誤報(bào)率。
1 相關(guān)研究工作
SQL注入漏洞最早在2000年左右被提出,其后學(xué)者們對SQL注入的檢測和防護(hù)展開了一系列的研究。
典型的一種SQL注入例子如下:
(1)用戶登錄Web應(yīng)用系統(tǒng)時(shí),需要進(jìn)行身份認(rèn)證,主要輸入用戶名和密碼兩個(gè)變量v_usr和v_pwd;
(2)Web系統(tǒng)執(zhí)行合法性檢查的SQL語句為:“Select * from users where username=‘”+v_usr+“’and password=‘”+ v_pwd+“’”,如果用戶登錄時(shí)用戶取為‘admin’ or 1=1--,那么合法性檢查的SQL語句等效于Select * from users where username=‘ admin’ or 1=1;
顯然,用戶名取‘admin’ or 1=1--時(shí),無論密碼輸入多少,都可以登錄系統(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)報(bào)文內(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],這一類檢測方法的檢測率和誤報(bào)率往往依賴于訓(xùn)練集的大小。
2 Java源代碼靜態(tài)掃描分析思路
程序靜態(tài)分析指在不執(zhí)行程序的情況下,通過自動掃描代碼發(fā)現(xiàn)隱含的程序隱患[6],具備執(zhí)行速度快、效率高等優(yōu)點(diǎn)。對源代碼進(jìn)行靜態(tài)分析時(shí)借鑒編譯技術(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個(gè)步驟。
具體實(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語句,最后釋放連接。由于建立連接過程一般比較耗時(shí)(一般需要幾十毫秒至幾百毫秒之間),而且Statement對象每次執(zhí)行SQL語句的解析和編譯也比較耗時(shí),如果涉及如下循環(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語句時(shí),DBMS需要對SQL語句進(jìn)行解析和編譯;而采用預(yù)編譯方式時(shí),DBMS只在第一次對SQL語句進(jìn)行解析和編譯。相比前者,后者更難注入SQL。原因在于,PreparedStatement對象執(zhí)行SQL語句前,需要設(shè)置“?”號對應(yīng)的變量,例如ps.setInt(1,變量v1)、ps.setLong(2,變量v2) 表示SQL語句中第一個(gè)“?”號是int型、值為“變量v1”;這樣在安全性上相當(dāng)于增加了一個(gè)類型匹配。
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ù)先建立一個(gè)數(shù)據(jù)庫連接池,在該連接池中預(yù)先存放一定數(shù)量的連接,當(dāng)需要建立數(shù)據(jù)庫連接時(shí),并非新建一個(gè)數(shù)據(jù)庫連接,而是從連接池中取出一個(gè)連接對象,然后對該連接對象進(jìn)行參數(shù)設(shè)置和使用,使用完畢后,并不釋放該連接,而是將連接對象的參數(shù)復(fù)位并回收。
采用連接池的應(yīng)用開發(fā)方式與常規(guī)方式的代碼結(jié)構(gòu)類似。區(qū)別是常規(guī)方式的連接對象在需要時(shí)才從堆中動態(tài)分配空間與數(shù)據(jù)庫建立連接,使用完畢后即斷開連接,等待Java的GC垃圾回收器發(fā)現(xiàn)該對象不再需要后,將回收連接對象的內(nèi)存空間;而采用連接池方式,則是在連接池中預(yù)先建立一個(gè)若干數(shù)據(jù)的連接對象,需要使用時(shí)才從連接池中取出一個(gè)連接對象(不是臨時(shí)創(chuàng)建,因此效率高),使用完畢后,并不斷開連接,而是將該連接對象歸還給對象池。通過一個(gè)“借用”和“歸還”的方式使得可以高效地對付頻繁的數(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á)式最終能追溯到某個(gè)特定的對象(如HttpServletRequest),則可以肯定這條路徑將可能存在SQL注入隱患。
3 Java源代碼SQL注入檢測算法
抽象語法樹AST(Abstract Syntax Tree)是程序源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式,樹上的每個(gè)節(jié)點(diǎn)表示源代碼中的一種結(jié)構(gòu),AST的好處在于不依賴于具體的方法和語言細(xì)節(jié)。對于源代碼的文法分析,首先進(jìn)行詞法分析,將源代碼中所有字符串從前至后逐個(gè)字符進(jìn)行掃描,并對每個(gè)“單詞”進(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中的每一個(gè)節(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),檢測下一個(gè)節(jié)點(diǎn);
(3) 確認(rèn)結(jié)點(diǎn),取得第一個(gè)參數(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),檢測下一個(gè)節(jié)點(diǎn)。若keyNode中所有結(jié)點(diǎn)都已檢測完成,則算法終止。
4 算法實(shí)驗(yàn)
為了驗(yàn)證本文提出的算法的有效性,以一個(gè)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注入漏洞,并報(bào)告相應(yīng)的路徑。
本文提出的SQL注入檢測算法已經(jīng)成功應(yīng)用于本單位信息系統(tǒng)的Java源代碼靜態(tài)檢測中。在新上線運(yùn)行的某Web系統(tǒng)(約10萬行代碼)靜態(tài)分析中,檢測時(shí)間僅為5 s,檢測準(zhǔn)確識別率高達(dá)90%,而誤報(bào)率僅有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.