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