李洋,劉婷
(湖南信息職業(yè)技術(shù)學(xué)院,湖南 長沙 410200)
隨著信息技術(shù)日益發(fā)展,越來越多的團(tuán)體或機(jī)構(gòu)將日常業(yè)務(wù)放到Web 系統(tǒng)上處理,這對Web 系統(tǒng)的安全性提出了更高更嚴(yán)的要求。然而,很多程序員在開發(fā)Web 系統(tǒng)的過程中沒有樹立足夠的安全意識(shí),使得這些Web 系統(tǒng)中或多或少存在著一些安全漏洞。其中一個(gè)比較常見的是SQL 注入漏洞,攻擊者利用SQL注入漏洞發(fā)起SQL 注入攻擊以達(dá)到竊取用戶敏感數(shù)據(jù)的目的[1]。
本文在分析目前主流的防SQL 注入攻擊技術(shù)的基礎(chǔ)上,提出了基于NDIS 中間層驅(qū)動(dòng)的防御SQL 注入攻擊的技術(shù),并對此進(jìn)行了詳細(xì)闡述。
SQL 注入攻擊是常見的攻擊Web 系統(tǒng)的手段之一,非法用戶利用程序員在編寫代碼時(shí)沒有對用戶輸入數(shù)據(jù)的合法性進(jìn)行漏洞判斷,通過提交一段針對性強(qiáng)的數(shù)據(jù)庫查詢代碼,就可根據(jù)程序返回的結(jié)果獲得想獲取的數(shù)據(jù)[2]。由于SQL 注入是從正常的WWW 端口訪問,表面上看起來與一般的Web 頁面訪問沒什么區(qū)別,因此部署在網(wǎng)絡(luò)上的防御設(shè)備不會(huì)對這種滲透式的攻擊發(fā)出警報(bào),SQL 注入也就很難在第一時(shí)間被發(fā)現(xiàn)。近年來,SQL 注入攻擊已經(jīng)從簡單的URL(Uniform Resource Locator,統(tǒng)一資源定位器)直接注入和表單注入發(fā)展成為對HTTP(Hyper Text Transfer Protocol,超文本傳輸)協(xié)議各個(gè)字段進(jìn)行注入,SQL注入帶來的威脅仍不可小視。
SQL 注入攻擊有一些典型的攻擊方式,下面通過一個(gè)例子來說明SQL 注入攻擊的過程。以下是一個(gè)Web 系統(tǒng)中的部分代碼:
String uID;
String sQuery;
uID=request.getParameter(“userCode”);
sQuery= “ select username from users where userid=’”+uID+”’”;
用戶利用http://infoweb.com/username.do?user Code=mycode 進(jìn)行查詢工作,
Web 系統(tǒng)將獲得的用戶輸入的參數(shù)傳入數(shù)據(jù)庫并執(zhí)行如下的SQL 語句:
select username from users where userid=’mycode’
這是一次正常的查詢操作,通過用戶ID 查找對應(yīng)的用戶名。但由于參數(shù)值沒有進(jìn)行任何處理,攻擊者通過嵌入附加SQL 語句的方式來修改查詢字符串的值,從而改變原先的語句表達(dá)邏輯,例如:http://infoweb.com/username.do?userCode=mycode‘;delete from users;--。
此時(shí)WEB 系統(tǒng)將獲得的用戶輸入的參數(shù)傳入數(shù)據(jù)庫后,數(shù)據(jù)庫將執(zhí)行如下3 條SQL 語句:
select username from users where userid=’mycode’;
delete from users;
--‘
可以看出,“;”和“--”字符終止了當(dāng)前的SQL語句和,結(jié)合添加的注釋將原來正常的SQL 查詢語句修改成3 條SQL 語句,其中第二條SQL 語句將user表中的數(shù)據(jù)全部刪除。造成這種情況的原因是攻擊者利用代碼缺陷重新構(gòu)造了SQL 語句,導(dǎo)致原有的邏輯變?yōu)椴樵僽sers 表并刪除users 表中的數(shù)據(jù),給數(shù)據(jù)的所有者造成巨大的損失。
SQL 注入攻擊語句具有靈活多變的特點(diǎn),因此,需要通過分析大量的攻擊語句來歸納總結(jié)這些語句的特征。以下是3 種最常見的完成某項(xiàng)攻擊目的的語句的特征:①判斷注入點(diǎn)存在與否。攻擊者嵌入“and 1=1”返回正確頁面,嵌入“and 1=2”返回錯(cuò)誤頁面,則說明此處大概率存在注入點(diǎn)。輸入and 1=1,and 1=2。②猜測數(shù)據(jù)庫表名。攻擊者嵌入語句后返回正常頁面,則說明數(shù)據(jù)庫中存在該表。and 0<>(select count(*)from userinfo) ---判斷是否存在userinfo 這張表。③猜解庫表中字段。攻擊者嵌入語句后返回正常頁面,則說明庫表中存在相應(yīng)的字段。輸入and 0<>(select count(username) from userinfo) ---猜測userinfo 這張表中是否存在username 這個(gè)字段。
采用預(yù)編譯技術(shù)。使用預(yù)編譯的SQL 語句進(jìn)行參數(shù)化查詢,將帶“?”的SQL 語句發(fā)送給DBMS(Database Management System,數(shù)據(jù)庫管理系統(tǒng))完成解析、檢查、編譯等工作,再把實(shí)際值賦給“?”,最后將“?”這個(gè)變量傳給SQL 語句。預(yù)編譯技術(shù)能夠有效防范惡意攻擊者利用拼接SQL 語句產(chǎn)生的漏洞進(jìn)行的SQL 注入攻擊。
嚴(yán)格控制數(shù)據(jù)類型。對強(qiáng)類型語言中一般不存在數(shù)字型注入,因?yàn)榻邮諗?shù)字時(shí),大多都會(huì)進(jìn)行整型(int)數(shù)據(jù)類型轉(zhuǎn)換。對于弱類型語言需要進(jìn)行數(shù)字類型檢查,防止數(shù)字型注入。
對特殊字符進(jìn)行轉(zhuǎn)義。手動(dòng)或者利用安全函數(shù)對諸如“‘”“#”等特殊字符進(jìn)行轉(zhuǎn)義,防止惡意攻擊者利用特殊字符閉合SQL 語句達(dá)到SQL 注入的目的。
上述技術(shù)手段的使用在很大程度上能夠有效防范SQL 注入攻擊,但在實(shí)際過程仍存在著以下困難:①不能動(dòng)態(tài)地防范SQL 注入攻擊。如果Web 系統(tǒng)上線后才發(fā)現(xiàn)仍然有部分SQL 注入攻擊語句能夠發(fā)生作用,那就說明系統(tǒng)代碼中還存在著未檢查出的SQL 注入攻擊漏洞,這時(shí)為了安全起見必須中斷Web 系統(tǒng)的正常運(yùn)行并立刻對原有代碼進(jìn)行修改后使之生效,在此期間由于系統(tǒng)中斷不可避免地會(huì)帶來一定的損失。②對現(xiàn)存的大量Web 應(yīng)用不適用。目前互聯(lián)網(wǎng)上仍存在著成千上萬的早期開發(fā)的Web 系統(tǒng),受限于當(dāng)時(shí)的技術(shù)水平,這些Web 系統(tǒng)中或多或少存在著一些SQL注入攻擊漏洞,如果將這些系統(tǒng)的源代碼進(jìn)行一一修改,那耗費(fèi)的人力和時(shí)間是不能接受的。
通過以上分析可知,傳統(tǒng)的防范SQL 注入攻擊手段存在著不足之處。鑒于此,本文提出將NDIS 中間層驅(qū)動(dòng)技術(shù)應(yīng)用到防SQL 注入攻擊中,通過對發(fā)往Web 系統(tǒng)的HTTP 數(shù)據(jù)報(bào)進(jìn)行分析,判斷其中是否存在SQL 注入攻擊行為,對攻擊行為采取相應(yīng)的防御措施。此技術(shù)最大的優(yōu)勢在于檢測和防御期間不需要暫停Web 系統(tǒng)的運(yùn)行,同時(shí)也不需要對Web 系統(tǒng)本身的代碼做任何修改。
NDIS 的全稱為網(wǎng)絡(luò)驅(qū)動(dòng)程序接口規(guī)范,在數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層和傳輸層均給出了詳細(xì)的通信接口規(guī)范,如圖1 所示。
圖1 NDIS 驅(qū)動(dòng)在Windows 中的位置
NDIS 提供了一個(gè)完整的開發(fā)環(huán)境,程序員只需要利用NDIS 提供的函數(shù)就能夠順利編寫出滿足自身業(yè)務(wù)需求的網(wǎng)絡(luò)驅(qū)動(dòng)程序[3]。
NDIS 對以下3 種網(wǎng)絡(luò)驅(qū)動(dòng)程序提供支持:①網(wǎng)卡驅(qū)動(dòng)程序,又稱為微端口驅(qū)動(dòng)程序,負(fù)責(zé)將接收到的數(shù)據(jù)包轉(zhuǎn)發(fā)給上層驅(qū)動(dòng)程序和接收來自上層驅(qū)動(dòng)程序發(fā)過來的數(shù)據(jù)包。②中間驅(qū)動(dòng)程序,又稱為NDIS 中間層驅(qū)動(dòng),它位于網(wǎng)卡驅(qū)動(dòng)程序和協(xié)議驅(qū)動(dòng)程序之間,既向網(wǎng)卡驅(qū)動(dòng)程序展現(xiàn)協(xié)議特性,又向協(xié)議驅(qū)動(dòng)程序展現(xiàn)小端口特性。因此網(wǎng)卡驅(qū)動(dòng)程序把它看作協(xié)議驅(qū)動(dòng)程序,協(xié)議驅(qū)動(dòng)程序把它看作網(wǎng)卡驅(qū)動(dòng)程序,中間驅(qū)動(dòng)程序?qū)τ谶@二者來說是透明的。③協(xié)議驅(qū)動(dòng)程序。實(shí)現(xiàn)并執(zhí)行具體的諸如TCP/IP/IPX/SPX 等網(wǎng)絡(luò)協(xié)議,對網(wǎng)卡驅(qū)動(dòng)程序或者中間驅(qū)動(dòng)程序發(fā)來的數(shù)據(jù)包進(jìn)行協(xié)議解析。
NDIS 中間層驅(qū)動(dòng)獲取來自網(wǎng)卡的原始數(shù)據(jù)包,NDIS 中間層驅(qū)動(dòng)的工作過程如圖2 所示。
圖2 NDIS 中間層驅(qū)動(dòng)工作過程圖
NDIS 中間層驅(qū)動(dòng)工作在網(wǎng)卡驅(qū)動(dòng)程序的MINIPORT 接口和協(xié)議驅(qū)動(dòng)程序的PROTOCOL 接口之間,向下導(dǎo)出一個(gè)PROTOCOL 接口與網(wǎng)卡驅(qū)動(dòng)程序交互,向上則導(dǎo)出一個(gè)MINIPORT 接口與協(xié)議驅(qū)動(dòng)程序交互,這樣就將自身透明地插入到了網(wǎng)卡驅(qū)動(dòng)程序與協(xié)議驅(qū)動(dòng)程序之間。當(dāng)網(wǎng)卡驅(qū)動(dòng)程序接收到底層傳上來的數(shù)據(jù)包后,會(huì)將數(shù)據(jù)包從自身的MINIPORT 接口發(fā)送到NDIS 中間層驅(qū)動(dòng)的PROTOCOL 接口,NDIS中間層驅(qū)動(dòng)接收到數(shù)據(jù)包后可以對數(shù)據(jù)包的內(nèi)容進(jìn)行處理,然后將數(shù)據(jù)包從自身的MINIPORT 接口發(fā)送到協(xié)議驅(qū)動(dòng)程序的PROTOCOL 接口,這樣NDIS 中間層驅(qū)動(dòng)就完成了一次數(shù)據(jù)包的傳遞工作。
由于從零開發(fā)一個(gè)完整的NDIS 中間層驅(qū)動(dòng)是一件復(fù)雜且難度很高的工作,因此Windows 驅(qū)動(dòng)開發(fā)工具WINDDK 自帶了名為PASSTHRU 的開發(fā)框架,它作為NDIS 中間層驅(qū)動(dòng)的一個(gè)實(shí)例,完成了一個(gè)NDIS中間層驅(qū)動(dòng)所應(yīng)具有的基本功能,程序員只需在此框架上添加合適的代碼就能很容易地開發(fā)出滿足特定功能的NDIS 中間層驅(qū)動(dòng)。
PASSTHRU 框架中的 PtReceive 函數(shù)和PtReceivePacket 函數(shù)負(fù)責(zé)完成接收數(shù)據(jù)包的工作,接收到的數(shù)據(jù)包存放在一個(gè)或多個(gè)名為NDIS_BUFFER的結(jié)構(gòu)體中,其中每個(gè)結(jié)構(gòu)體記錄了數(shù)據(jù)包的一部分內(nèi)容及數(shù)據(jù)包內(nèi)容的結(jié)構(gòu)體的地址。因此程序員在這2個(gè)函數(shù)中添加適當(dāng)?shù)拇a,通過順序遍歷數(shù)據(jù)包對應(yīng)的所有結(jié)構(gòu)體就可以獲取到NDIS 中間層驅(qū)動(dòng)接收的數(shù)據(jù)包的內(nèi)容。以下是PtReceivePtReceivePacket 函數(shù)中添加的獲取數(shù)據(jù)包的關(guān)鍵代碼:
NdisQueryPacket(Packet,&PhysicalBufferCount,&B ufferCount,&NdisBuffer,&TotalPacketLength);
NdisAllocateMemory(&MyBuffer,4096,0,HighestA cceptableMax);
NdisZeroMemory(MyBuffer,4096);
NdisQueryBufferSafe(NdisOwnBuffer,&TmpBuffer,
&CpySize,NormalPagePriority);
NdisMoveMemory(MyBuffer,TmpBuffer,CpySize);
Offset=CpySize;
while(1)
{
if(NdisOwnBuffer==Packet->Private.Tail)
break;
NdisOwnBuffer=NdisOwnBuffer->Next;if(NdisOwnBuffer==NULL)
break;
NdisQueryBufferSafe(NdisOwnBuffer,&TmpBuffer,&CpySize,NormalPagePriority);
NdisMoveMemory(MyBuffer +Offset,TmpBuffer,CpySize);
Offset+=CpySize;
}
經(jīng)過以上處理,MyBuffer 中存放了NDIS 中間層驅(qū)動(dòng)接收的數(shù)據(jù)包的副本,數(shù)據(jù)包獲取工作完成。
由于SQL 注入攻擊語句是通過HTTP 數(shù)據(jù)報(bào)提交給服務(wù)器的,因此需要對之前的MyBuffer 中的內(nèi)容進(jìn)行解析,判斷獲取的數(shù)據(jù)包中是否攜帶了HTTP 數(shù)據(jù)報(bào),作為是否進(jìn)行后續(xù)的SQL注入攻擊檢測工作的依據(jù)。
解析數(shù)據(jù)包的關(guān)鍵在于獲取數(shù)據(jù)包中攜帶的數(shù)據(jù)報(bào)首部信息。如果通過解析,數(shù)據(jù)包中包含了IP 首部、TCP 首部及TCP 首部中目的端口號(hào)字段為80,則數(shù)據(jù)包中攜帶了HTTP 數(shù)據(jù)報(bào),接下來將對HTTP 數(shù)據(jù)報(bào)進(jìn)行解析。以下是NDIS 中間層驅(qū)動(dòng)中解析數(shù)據(jù)包的關(guān)鍵代碼:
if(MyBuffer[12] == 0x08 && MyBuffer[13] ==0x00 && MyBuffer[23] == 0x06 &&MyBuffer[14+ipheaderlen+2] == 0x00 &&MyBuffer[14+ipheaderlen+3]==0x50)
HTTP 數(shù)據(jù)報(bào)攜帶了客戶端提交給服務(wù)器的數(shù)據(jù),這些數(shù)據(jù)中可能包含SQL 注入攻擊語句,因此需要通過解析HTTP 數(shù)據(jù)報(bào)獲取客戶端提交的數(shù)據(jù)。HTTP數(shù)據(jù)報(bào)的請求方法有“GET”和“POST”。根據(jù)HTTP數(shù)據(jù)報(bào)協(xié)議,如果請求方法為“GET”,則提交的數(shù)據(jù)包含在URL 部分;如果請求方法為“POST”,則提交的數(shù)據(jù)包含在請求數(shù)據(jù)部分,如圖3 所示。
通過解析HTTP 數(shù)據(jù)報(bào)可以獲得客戶端提交的數(shù)據(jù),接下來將對提交的數(shù)據(jù)進(jìn)行SQL 注入攻擊檢測。
圖3 HTTP 數(shù)據(jù)報(bào)
正則表達(dá)式實(shí)際上是一種邏輯公式,它表達(dá)的是匹配字符串的邏輯,即通過組合事先定義好的特定的字符構(gòu)建一個(gè)“規(guī)則表達(dá)式”,然后利用這個(gè)“規(guī)則表達(dá)式”完成對字符串的匹配工作。正則表達(dá)式有基本元字符、次數(shù)元字符、位置元字符和特殊元字符這幾類常見元字符,利用這些元字符以及適當(dāng)?shù)钠胀ㄗ址梢詷?gòu)造出用以描述SQL 注入攻擊語句的正則表達(dá)式。常見的SQL 注入攻擊語句及其正則表達(dá)式如表1 所示。
表1 常見的SQL 注入攻擊語句及其正則表達(dá)式
一旦這些事先定義好的SQL 注入攻擊語句的正則表達(dá)式匹配到了客戶端通過HTTP數(shù)據(jù)報(bào)提交的數(shù)據(jù),則說明數(shù)據(jù)中嵌入了SQL 注入攻擊語句,此時(shí)HTTP數(shù)據(jù)報(bào)將被NDIS 中間層驅(qū)動(dòng)丟棄,從而達(dá)到了保護(hù)Web 系統(tǒng)免遭攻擊的目的。
本文首先介紹了SQL 注入攻擊和NDIS 中間層驅(qū)動(dòng)的工作原理,之后對NDIS 中間層驅(qū)動(dòng)如何運(yùn)用在SQL 注入攻擊的檢測和防御方面進(jìn)行了詳細(xì)描述,具有一定的參考價(jià)值。