付熙徐,龔希章
(上海海洋大學(xué) 現(xiàn)代信息與教育技術(shù)中心,上海 201306)
SQL注入攻擊是通過在應(yīng)用程序輸入中插入部分SQL語(yǔ)句,改變應(yīng)用程序中實(shí)際執(zhí)行的SQL語(yǔ)句,以達(dá)到繞開驗(yàn)證、篡改數(shù)據(jù)、控制被攻擊系統(tǒng)之目的的攻擊[1-2]。由于目前很多應(yīng)用系統(tǒng)采用的都是基于關(guān)系數(shù)據(jù)庫(kù)通過SQL語(yǔ)句操縱數(shù)據(jù)的模式,因而SQL注入攻擊被視為對(duì)Web應(yīng)用威脅最大的攻擊方式之一。
阻止注入攻擊的方式通常是對(duì)輸入進(jìn)行判斷,從而阻止非法字符的輸入。然而,如果需要判斷的攻擊字符串過多,就容易消耗過多的資源,尤其當(dāng)驗(yàn)證工作在需要處理大量信息的服務(wù)器上進(jìn)行時(shí),將嚴(yán)重影響服務(wù)器的性能。為此,本文對(duì)注入攻擊的原理和注入攻擊語(yǔ)句進(jìn)行了分析,針對(duì)不同的輸入設(shè)定了不同的處理方法,最終使得需要判斷的攻擊字符集達(dá)到最小,從而最大限度地減輕服務(wù)器的壓力。
SQL注入攻擊的攻擊方法是通過非法輸入改變系統(tǒng)中應(yīng)執(zhí)行的SQL語(yǔ)句。如下面的語(yǔ)句:
select * from user where username=′user′ and password=′123′
其目的是返回用戶表中用戶名為“user”,且密碼為“123”的用戶,通常用于系統(tǒng)用戶驗(yàn)證。正常輸入為合法的用戶名和密碼,但如果在密碼部分輸入如下字符串則可以達(dá)到跳過密碼驗(yàn)證的目的。
1’ or ‘1’=’1
SQL語(yǔ)句加入該字符串后,變成如下形式:
select * from user where username=′user′ and password=′ 1′ or ′1′=′1′
則失去了校驗(yàn)密碼的功能。同理,攻擊者也可插入SQL語(yǔ)句對(duì)數(shù)據(jù)和數(shù)據(jù)庫(kù)結(jié)構(gòu)進(jìn)行修改和刪除,從而對(duì)數(shù)據(jù)和系統(tǒng)造成嚴(yán)重破壞;更有甚者,攻擊者可以通過數(shù)據(jù)庫(kù)系統(tǒng)執(zhí)行操作系統(tǒng)命令,給系統(tǒng)造成更嚴(yán)重的威脅[1]。另外,由于數(shù)據(jù)庫(kù)和SQL的廣泛使用,注入漏洞的范圍也擴(kuò)展到云和嵌入式系統(tǒng)中[3]。
總的來說,SQL注入攻擊的方式有兩種:一種是直接在系統(tǒng)和網(wǎng)站客戶端的表單中輸入注入攻擊語(yǔ)句,另一種是通過偽造的輸入頁(yè)面或網(wǎng)絡(luò)工具將攻擊語(yǔ)句直接提交到應(yīng)用服務(wù)器中進(jìn)行攻擊,如圖1所示。
注入攻擊的防御通常是通過屏蔽敏感字符和詞匯為主[1,4]。根據(jù)注入攻擊的方式,防御可分為客戶端防御和服務(wù)器端防御。
客戶端防御是在客戶端對(duì)數(shù)據(jù)進(jìn)行限制和驗(yàn)證,服務(wù)器端防御是在服務(wù)端進(jìn)行判斷。前者需要判斷的可用于攻擊的字符串較多[1],也不能防止盲注攻擊;后者可以防止盲注攻擊,但服務(wù)端資源開銷較大。
為了提高注入攻擊防御的準(zhǔn)確率,通常采用數(shù)據(jù)挖掘的方式對(duì)注入攻擊進(jìn)行確認(rèn)[5]。但由于精準(zhǔn)的確認(rèn)開銷較大,為了減輕服務(wù)器的壓力,可以將精準(zhǔn)確認(rèn)與SQL注入的阻止錯(cuò)開進(jìn)行。另有一些防御方式,如基于正則表達(dá)式的防御等,其本質(zhì)上也是對(duì)異常輸入的防御[6]。
圖1 SQL注入攻擊的方式Fig.1 SQL injection methods
除去由于防御方式不同導(dǎo)致的盲注攻擊防御問題和系統(tǒng)開銷過大問題以外,對(duì)于合法內(nèi)容的過渡防御、特殊字符串的替換及正確顯示、二次注入攻擊的防御也是目前各種防御方法需要改進(jìn)的重要問題。
圖2顯示的是某系統(tǒng)對(duì)潛在注入數(shù)據(jù)進(jìn)行的攔截。圖2中,提交的數(shù)據(jù)并非注入攻擊數(shù)據(jù),但由于其包含了防御系統(tǒng)禁止的關(guān)鍵字而被禁止輸入。事實(shí)上,一些包含非法字符的輸入仍然是合法有意義的內(nèi)容,應(yīng)予以保存和顯示。
圖2 對(duì)SQL注入攻擊的過度防御Fig.2 Excessive defense for SQL injection attack
通常來說,防止注入攻擊的關(guān)鍵問題在于判斷哪些字符串可以用于注入攻擊,而很多反注入方案使用了較大的關(guān)鍵字符集,致使系統(tǒng)開銷較大。本文對(duì)注入攻擊語(yǔ)句和注入攻擊關(guān)鍵字定義如下:
定義1 注入攻擊語(yǔ)句是一個(gè)字符串,將該字符串作為字段的值插入SQL語(yǔ)句中會(huì)引發(fā)SQL執(zhí)行錯(cuò)誤或改變SQL語(yǔ)句執(zhí)行的目的。本文將一個(gè)注入攻擊語(yǔ)句記為a,注入攻擊語(yǔ)句集合計(jì)為A。
定義2 極小注入關(guān)鍵字符串集是一個(gè)字符串的集合K,k為K的元素,則K是符合式(1)的最小集合。
(?k∈K(k?s))→s?A
(1)
反注入攻擊的基本要求一方面是防止入侵者修改SQL語(yǔ)句,執(zhí)行跳過認(rèn)證、篡改數(shù)據(jù)庫(kù)、攻擊操作系統(tǒng)等操作;另一方面,系統(tǒng)應(yīng)該把合法的輸入保存到數(shù)據(jù)庫(kù)中,并在顯示時(shí)盡可能顯示輸入的內(nèi)容。
對(duì)于防止注入攻擊,通常的要求是防止所有的注入攻擊。對(duì)于正確顯示的要求,由于可能將文本中出現(xiàn)的字符替換成敏感字符,故不一定可以完全恢復(fù)。通常輸入的恢復(fù)率可以用式(2)來評(píng)價(jià)。
(2)
在服務(wù)器端對(duì)于字符型字段,若在程序中SQL語(yǔ)句字符字段前后用單引號(hào)標(biāo)識(shí),則一個(gè)注入攻擊語(yǔ)句可用式(3)表示。
L=c*′c*
(3)
其中c為任意字符。
很顯然K={′}是一個(gè)極小注入關(guān)鍵字符串集,也就是說,對(duì)字符型數(shù)據(jù)字段,只需判斷輸入是否含單引號(hào)即可確認(rèn)是否為注入攻擊語(yǔ)句;而對(duì)于非字符型輸入如數(shù)字、日期時(shí)間、文件等,只要確定輸入類型正確即可防止注入。
輕量級(jí)服務(wù)端反注入編碼流程如圖3所示,該流程可保證系統(tǒng)不受注入攻擊。
圖3 輕量級(jí)服務(wù)端反注入編碼Fig.3 Lightweight server side anti-injection encoding
在顯示時(shí),只需將替換字符串替換回注入攻擊字符串即可還原輸入字符串。
為了提高回顯準(zhǔn)確率,可以使用最不頻繁出現(xiàn)的字符串替換輸入中出現(xiàn)的注入攻擊關(guān)鍵字符串。按照文本挖掘中的定義,含n個(gè)字符的字符串稱為n-gram字符串,n-gram字符串的集合稱為n-gram集合[7]。最不頻繁字符串集獲取算法如下:
輸入 候選字符集C,訓(xùn)練文本集T,頻數(shù)閾值f,最不頻繁字符串?dāng)?shù)量n,字符串最大長(zhǎng)度lmax
輸出 最不頻繁字符串集U
Begin
S=C
l=1
While l For each s in S For each t in T 查找u在t中頻數(shù)ft fu=fu+ft Next t if fu if U中元素個(gè)數(shù)>=n then return U Next s For each s in S For each c in C s′=s+c(取字符連接到字符串s后面) 將s′加入S Next c 將s從S中移除 Next s Loop End 該算法復(fù)雜度為O(n3),但在實(shí)際運(yùn)行中,通常在1-gram和2-gram中就可以找到足夠的最不頻繁字符串,因此程序?qū)嶋H復(fù)雜度接近O(n2)。另外,還可以根據(jù)出現(xiàn)頻數(shù)對(duì)候選字符和n-gram集進(jìn)行排序或剪枝,使得程序運(yùn)行更快。 數(shù)據(jù)在顯示時(shí)需將用于替換的字符串替換回原來的注入關(guān)鍵字,只需根據(jù)替換對(duì)應(yīng)表完成替換即可。由于原文本中可能含有用于替換的字符串,因此仍然可能會(huì)導(dǎo)致部分顯示錯(cuò)誤。例如方案中用@字符替換單引號(hào),但文本中有一個(gè)電子郵件地址,該地址中的@字符也會(huì)被錯(cuò)誤地替換為單引號(hào)。 由于數(shù)據(jù)通常都是用于顯示,故替換后數(shù)據(jù)包含一些注入攻擊語(yǔ)句也不影響系統(tǒng)安全;但若數(shù)據(jù)被用于系統(tǒng)的控制、驗(yàn)證等,則可能造成二階注入攻擊[4]。為應(yīng)對(duì)二階注入攻擊,最好的方式是程序再次判斷數(shù)據(jù)是否合法,也可不對(duì)用于顯示以外功能的數(shù)據(jù)進(jìn)行注入關(guān)鍵字符串替換。 為測(cè)試本文極小關(guān)鍵字法的有效性,建立了一套簡(jiǎn)單的系統(tǒng)用于測(cè)試。系統(tǒng)使用Apache 2.2作為Web服務(wù)器,php 5.5作為服務(wù)端編程語(yǔ)言,MySQL 5.6作為數(shù)據(jù)庫(kù)。系統(tǒng)數(shù)據(jù)庫(kù)只含一個(gè)表,表名data,表結(jié)構(gòu)如表1所示。 表1測(cè)試系統(tǒng)數(shù)據(jù)庫(kù)結(jié)構(gòu) 字段名稱字段類型和長(zhǎng)度字段含義和說明idint(1)記錄編號(hào),唯一標(biāo)識(shí)contentvarchar(200)消息內(nèi)容injectbool是否含注入字符 注: (1) 自增型變量,長(zhǎng)度由系統(tǒng)決定 系統(tǒng)中需要輸入的量為消息內(nèi)容,即content字段。若成功插入數(shù)據(jù)庫(kù),則讀取數(shù)據(jù)庫(kù)中內(nèi)容,恢復(fù)其中單引號(hào)字符進(jìn)行顯示。 進(jìn)行測(cè)試的注入字符串為 ′);drop table data;- 若注入成功,則data表被刪除,系統(tǒng)返回錯(cuò)誤;否則,返回下內(nèi)容: Content: ′);drop table data;- 經(jīng)測(cè)試,該系統(tǒng)可以防御各種形式的注入式攻擊,包括從輸入界面進(jìn)行的攻擊、盲注以及文獻(xiàn)[1,4]中提及的用不同形式替換注入攻擊字符的攻擊。 使用文獻(xiàn)[4]中的敏感字符集方法與本文極小關(guān)鍵安法進(jìn)行編碼和效率對(duì)比,結(jié)果如圖4所示。輸入的文本長(zhǎng)度分別為:短文本約為10字符左右,中等長(zhǎng)度文本在100字符左右,長(zhǎng)文本在1 000字符左右。 圖4 本文方法與傳統(tǒng)防御方法的開銷對(duì)比Fig.4 System cost compare of method in this paper and traditional method 由圖4可知,由于需要判斷的敏感注入字符串較少,極小關(guān)鍵字法系統(tǒng)開銷明顯要小很多,極小關(guān)鍵字法的效率更高。 為測(cè)試攻擊關(guān)鍵字的替換與回顯情況,采用兩個(gè)不同文本庫(kù)進(jìn)行測(cè)試,一個(gè)是美國(guó)開放文本語(yǔ)料庫(kù)[8],主要包含口語(yǔ)、小說、期刊、信件等內(nèi)容。在該庫(kù)中隨機(jī)選取30篇材料作為測(cè)試集,運(yùn)行程序后未發(fā)現(xiàn)有@、#等字符;再隨機(jī)選取100篇材料,使用@字符替換單引號(hào),然后將所有@字符替換回單引號(hào),恢復(fù)率為100%。 由于實(shí)際輸入和小說、期刊等內(nèi)容有所不同,實(shí)際輸入可能會(huì)包含較多的特殊字符,因此,本文還引入了一個(gè)微博庫(kù)進(jìn)行測(cè)試[9]。在微博庫(kù)中隨機(jī)選取20篇材料作為測(cè)試集,總字符數(shù)2 479 580個(gè),其中頻率較低的一些字符串如表2。 從表2可知,2字符組合的字符串中已經(jīng)可以找出較多的用于替換的字符串了。 表2用于替換注入關(guān)鍵字候選字符串 字符串類型訓(xùn)練集出現(xiàn)頻率^1-gram12+1-gram32~1-gram39@1-gram107#1-gram109+~2-gram0-^2-gram0~#2-gram0~^2-gram0@-2-gram0~-2-gram1 為測(cè)試替換的恢復(fù)率,在微博庫(kù)中隨機(jī)選取100篇材料,總字符數(shù)12 397 965個(gè),其中單引號(hào)32 293個(gè);使用候選字符串替換單引號(hào),然后將所有候選字符串替換回單引號(hào)。部分候選字符串恢復(fù)率如表3所示。 表3候選字符串恢復(fù)率 字符串恢復(fù)率R+~100-^100~#100~^100@-99.99998~-99.9997 由表3可知,2-gram候選字符串恢復(fù)率極高,對(duì)內(nèi)容影響幾可忽略不計(jì)。 對(duì)于不同的系統(tǒng),由于輸入類型的不同,可以采用不同的訓(xùn)練集來獲取最不頻繁的字符串。對(duì)于公文系統(tǒng)只需采用前面的文章小說類訓(xùn)練集即可,而論壇類系統(tǒng)則建議使用字符包含范圍更廣的訓(xùn)練集如微博、論壇的文字集合等。 通過對(duì)反注入攻擊方法的討論,得出了只要禁止字符串定義的邊界字符(本文中是單引號(hào))出現(xiàn)在字符型數(shù)據(jù)中,并對(duì)其他類型輸入進(jìn)行類型判斷即可防止SQL注入攻擊的結(jié)論。另外,關(guān)于數(shù)據(jù)的還原和顯示,提出了使用最不頻繁字符串替換注入關(guān)鍵字符串集的方法。經(jīng)實(shí)驗(yàn),本文的方案能夠有效防止SQL注入攻擊,并將用戶的輸入正確地顯示出來。該方案也可推廣到其他控制操縱語(yǔ)句與數(shù)據(jù)在同一字符串中的系統(tǒng),如DMSQL等。3.3 數(shù)據(jù)顯示和對(duì)二階注入攻擊的防御
4 實(shí)驗(yàn)與結(jié)果
4.1 實(shí)驗(yàn)概述
Table1Database structure of test system4.2 程序性能實(shí)驗(yàn)及結(jié)果
4.3 攻擊關(guān)鍵字替換及回顯
Table2Candidate strings to replace injection keys
Table3Recover rate for candidate strings %5 結(jié)束語(yǔ)