摘要:SQL注入是Web應(yīng)用中常見(jiàn)的一種針對(duì)數(shù)據(jù)庫(kù)層的攻擊方法。該文分析了SQL注入的原理和攻擊方法,總結(jié)了實(shí)踐中常用的針對(duì)SQL注入的防范措施。此防護(hù)措施經(jīng)適當(dāng)修改即可用于多種平臺(tái)類型的Web應(yīng)用中。
關(guān)鍵詞:SQL注入;攻擊;防護(hù)措施
中圖分類號(hào):TP393文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2009)04-0777-04
The Research of SQL Injection Attack and Prevention Method
SHI Ying1, KONG Qiao2
(1.Navy Institute of Compute Technology, Beijing 100841, China; 2 Navy Testing Center of Weapon Equipment, Beijing 100161, China)
Abstract: SQL injection is a common attack method which is aimed at database in web application. In this paper, the principle and the attack method of the SQL injection are analyzed, and then the effective measures of prevention are summarized. These measures can also be used in many other web environments by modified.
Key words: SQL injection; attack; prevention measures
1 引言
SQL注入(SQL Injection)漏洞是存在于應(yīng)用程序數(shù)據(jù)庫(kù)層的安全漏洞,攻擊者可以利用這個(gè)漏洞在輸入的資料字串中夾帶SQL指令,一旦應(yīng)用程序忽略了檢查,這些夾帶進(jìn)去的指令就會(huì)被數(shù)據(jù)庫(kù)服務(wù)器誤認(rèn)為正常的SQL指令而執(zhí)行,從而導(dǎo)致數(shù)據(jù)庫(kù)結(jié)構(gòu)以及系統(tǒng)資料外泄,最終使系統(tǒng)遭到破壞。
由于SQL注入是從WWW端口訪問(wèn),而且表面看來(lái)跟一般的Web頁(yè)面訪問(wèn)沒(méi)有區(qū)別,所以多數(shù)防火墻不會(huì)對(duì)SQL注入發(fā)出警報(bào)。通過(guò)這種攻擊,攻擊者很容易獲得數(shù)據(jù)庫(kù)中的賬戶資料、密碼信息等,從而進(jìn)一步篡改系統(tǒng)管理員賬戶,在網(wǎng)頁(yè)中加入惡意鏈接以及XSS。經(jīng)由數(shù)據(jù)庫(kù)服務(wù)器提供的操作系統(tǒng)支持,攻擊者還能夠修改或控制操作系統(tǒng),破壞硬盤(pán)數(shù)據(jù),乃至癱瘓全系統(tǒng)。因此,開(kāi)發(fā)人員有必要對(duì)SQL注入有全面的了解,從而提高自身的安全意識(shí)和軟件的健壯性。本文分析了SQL注入的原理和攻擊方法,總結(jié)了一些有效的防護(hù)措施。所有代碼均在JSP+Tomcat/6.0.16+MySQL環(huán)境下運(yùn)行通過(guò)。
2 SQL注入的作用原理
很多Web站點(diǎn)都會(huì)利用用戶輸入的參數(shù)動(dòng)態(tài)生成SQL查詢請(qǐng)求。如果攻擊者在URL、表格域或其他的輸入域中輸入自己的SQL命令,而Web程序在組合SQL命令字串時(shí)未進(jìn)行嚴(yán)格的數(shù)據(jù)過(guò)濾,就有可能被插入惡意的SQL代碼。例如,某網(wǎng)站驗(yàn)證登錄者用戶名和密碼的SQL查詢代碼為:
strSQL = \" SELECT * FROM user WHERE (name = '\" + username +\" ' ) and ( pw = '\" + password + \" ' ) \";
若攻擊者惡意填入
username = \" ' OR ' 1 ' = ' 1 \";
passWord = \" ' OR ' 1 ' = ' 1 \";
此時(shí)原本的SQL查詢代碼被填為
strSQL = \" SELECT * FROM user WHERE ( name = ' ' OR ' 1 ' = ' 1 ' ) and ( pw = ' ' OR ' 1 ' = ' 1 ' ) \";
實(shí)際上運(yùn)行的SQL命令變?yōu)?/p>
strSQL = \" SELECT * FROM user \" ;
由此攻擊者達(dá)到無(wú)用戶名、密碼亦可登錄網(wǎng)站的目的。
3 SQL注入攻擊的方法
3.1 確定SQL注入攻擊的注入點(diǎn)
動(dòng)態(tài)網(wǎng)頁(yè)在利用傳入的參數(shù)與數(shù)據(jù)庫(kù)進(jìn)行存取交互時(shí),如果沒(méi)有對(duì)傳入的參數(shù)進(jìn)行必要的安全處理,就可能存在SQL注入漏洞。在此,本文模擬了一個(gè)功能為顯示文章信息的網(wǎng)頁(yè),通過(guò)傳遞ID號(hào)在網(wǎng)頁(yè)上顯示某一具體文章的索引號(hào)、名稱和作者,本網(wǎng)頁(yè)設(shè)計(jì)時(shí)未進(jìn)行任何安全處理措施。在瀏覽器地址欄輸入http://127.0.0.1:8080/article.jsp?id=1,頁(yè)面正常顯示:
1|article1|author1
當(dāng)?shù)刂份斎敫臑閔ttp://127.0.0.1:8080/article.jsp?id=1'時(shí),頁(yè)面出錯(cuò),返回錯(cuò)誤信息如下:
Root cause
javax.servlet.ServletException:com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:850)
……
NoteThe full stack trace of the root cause is available in the Apache Tomcat/6.0.16 logs.
通過(guò)這些錯(cuò)誤提示,攻擊者可獲得許多關(guān)鍵信息,如網(wǎng)站使用的是MySQL數(shù)據(jù)庫(kù),并且使用Tomcat/6.0.16作為應(yīng)用服務(wù)器。
3.2 獲取網(wǎng)站信息
1) 利用SQL語(yǔ)法中的UNION聯(lián)合查詢來(lái)猜測(cè)當(dāng)前表的字段數(shù)。
由于知道了網(wǎng)站使用的是MySQL數(shù)據(jù)庫(kù),故可利用MySQL的注釋字符“/*”來(lái)屏蔽后續(xù)SQL語(yǔ)句,輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1/*
測(cè)試表的字段數(shù)是否為1,頁(yè)面錯(cuò)誤,返回錯(cuò)誤信息:
Root cause
Javax.servlet.ServletException: java.sql.SQLException:The used SELECT statements have a different number of columns
說(shuō)明數(shù)據(jù)表的字段數(shù)不為1,不斷增加字段數(shù)進(jìn)行測(cè)試,直至輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,1,1 /*
頁(yè)面正常顯示,說(shuō)明當(dāng)前數(shù)據(jù)表的字段數(shù)位3。
2) 使用UNION猜測(cè)當(dāng)前數(shù)據(jù)庫(kù)中的其他數(shù)據(jù)表。在此我們依據(jù)經(jīng)驗(yàn)嘗試猜測(cè)是否有名為admin的表存在。輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,1,1 from admin /*
頁(yè)面顯示正常,說(shuō)明數(shù)據(jù)庫(kù)中的確存在名為admin的表。
接下來(lái)可以猜字段名。比如猜測(cè)表中是否含有名為id的字段,可以輸入
http://127.0.0.1:8080/article.jsp?id=1' union select id,1,1 from admin /*
頁(yè)面未報(bào)錯(cuò),正常顯示為
1|article1|author1
由此可知,確實(shí)有名為id的字段。攻擊者可使用技巧猜出所有字段名,再將猜出的字段名全部代入union查詢中,當(dāng)字段名正確時(shí)頁(yè)面會(huì)返回表內(nèi)信息,則攻擊者可獲得管理員賬號(hào)和密碼等重要信息。
3) 使用MySQL函數(shù)顯示或上傳文件。如果當(dāng)前頁(yè)面中連接數(shù)據(jù)庫(kù)的用戶擁有對(duì)文件的讀寫(xiě)權(quán)限,那么攻擊者可以利用函數(shù)load_file()在頁(yè)面中加載系統(tǒng)文件或查看系統(tǒng)目錄,例如可以在地址欄輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,load_file(0x633A5C626F6F742
E696E69),1 /*
其中0x633A5C626F6F742E696E69是路徑c:/boot.ini的十六進(jìn)制編碼,此時(shí)如果數(shù)據(jù)庫(kù)服務(wù)器為Windows XP操作系統(tǒng),則系統(tǒng)文件c:/boot.ini內(nèi)的重要信息就會(huì)被暴露到網(wǎng)頁(yè)上。
若數(shù)據(jù)庫(kù)服務(wù)器采用Unix操作系統(tǒng),并且用戶擁有路徑的讀取權(quán)限,則輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,load_file(/),1/*
此時(shí)操作系統(tǒng)的目錄結(jié)構(gòu)將暴露在頁(yè)面上,由此進(jìn)一步分析,攻擊者可得到Web路徑及Web管理地址。
得到了這些重要信息,攻擊者便可以利用outfile等函數(shù)輕松上傳各種木馬病毒,從而達(dá)到控制和攻擊網(wǎng)站的目的。
SQL注入手法多樣,方法靈活,危害巨大,但只要開(kāi)發(fā)者周密設(shè)計(jì),多方測(cè)試,不使網(wǎng)站存在SQL注入漏洞,則完全可以避免此類攻擊。
4 對(duì)于SQL注入攻擊的防范措施
一般來(lái)說(shuō),SQL注入的防范可以從兩方面著手,即服務(wù)器配置和軟件設(shè)計(jì)本身。在此我們主要對(duì)程序設(shè)計(jì)方面的防范措施進(jìn)行分析,對(duì)服務(wù)器配置方面的防范措施僅略作介紹。
4.1 使用參數(shù)化查詢方法實(shí)現(xiàn)資料存取功能
參數(shù)化查詢是指在構(gòu)造SQL語(yǔ)句時(shí),在需要填入數(shù)據(jù)的地方使用參數(shù)來(lái)代替真實(shí)的數(shù)據(jù)信息。此方法可有效防范SQL注入攻擊。
我們?nèi)砸詀rticle.jsp網(wǎng)頁(yè)為例,以下為采用參數(shù)化查詢的設(shè)計(jì)方法實(shí)現(xiàn)的jsp網(wǎng)頁(yè)代碼。其中,id為從網(wǎng)頁(yè)傳入的參數(shù)。
<%@ page import=\"java.util.*\"%>
<%@ page import=\"java.sql.*\" %>
<%
Connection conn=1;
PreparedStatement pstmt=1;//創(chuàng)建PreparedStatement對(duì)象
String dbUrl=\"jdbc:mysql://localhost/sy?user=rootpassword=root
useUnicode=truecharacterEncoding=UTF-8\";
try
{
Class.forName(\"com.mysql.jdbc.Driver\").newInstance();
conn=DriverManager.getConnection(dbUrl); //連接數(shù)據(jù)庫(kù)
String id=request.getParameter(\"id\");
if(id==1)
id=\"\";
String sql=\"select * from article where id=?\";
pstmt=conn.prepareStatement(sql);
pstmt.setString(1, id);//使用setXXX方法傳遞IN參數(shù)的值
ResultSet rs=pstmt.executeQuery(); //執(zhí)行PreparedStatement語(yǔ)句
while(rs.next())
{
out.print(rs.getInt(1) + \"|\");
out.print(rs.getString(2) + \"|\");
out.print(rs.getString(3));
out.print(\"\");
}
pstmt.close();
}
catch(SQLException ex)
{
System.out.println(\"SQLException:\" + ex.getMessage());
}
finally
{
try
{
if(conn!=1)
conn.close();
}
catch(SQLException ex)
{
System.out.println(\"SQLException:\" + ex.getMessage());
}
}
%>
在上述代碼中,PreparedStatement是JDBC中實(shí)現(xiàn)參數(shù)化查詢的接口,它繼承自Statement接口,又與之有所不同。PreparedStatement實(shí)例中包含已編譯過(guò)的SQL語(yǔ)句,而此SQL語(yǔ)句中可含有若干個(gè)未賦予具體值的IN參數(shù),它們以占位符的形式(如“?”)存在。在SQL語(yǔ)句執(zhí)行前,可通過(guò)setXXX的方法為這些參數(shù)傳入具體數(shù)值。也就是說(shuō),使用PreparedStatement接口時(shí),數(shù)據(jù)庫(kù)服務(wù)器不會(huì)將用戶傳入的參數(shù)內(nèi)容視為SQL指令的一部分來(lái)處理,而是在數(shù)據(jù)庫(kù)完成SQL指令的編譯后才套用參數(shù)執(zhí)行,因此即使參數(shù)中含有具破壞性的指令,也不會(huì)被數(shù)據(jù)庫(kù)執(zhí)行。使用參數(shù)化查詢?cè)O(shè)計(jì)是一種可靠的防范SQL注入攻擊的措施。
4.2 對(duì)傳入?yún)?shù)中的不安全字符進(jìn)行過(guò)濾
為了避免惡意參數(shù)的侵害,還可以采用字符過(guò)濾的方法,即對(duì)網(wǎng)站前臺(tái)傳遞給數(shù)據(jù)庫(kù)的參數(shù)進(jìn)行過(guò)濾,替換不安全字符。以下代碼將此過(guò)濾功能設(shè)計(jì)成javabean,在頁(yè)面程序中調(diào)用此javabean實(shí)現(xiàn)過(guò)濾功能。
public class Filter
{
private String str;
public Filter()
{}
public void setStr(String stri)
{
this.str=stri;
}
public String getStr()
{
return this.str;
}
public void TransactSQLInjection()
{
String inj_str =\",|'|;|--|/*|and|exec|insert|select|delete|
update|count|load_file|outfile|mid|or|union\";
String inj_stra[] = inj_str.split(\"|\");
String rep_str = \",|'|;|--|/*|and|exec|insert|select|delete|update|count|load_file|outfile|mid|or|union\";
String rep_stra[] = rep_str.split(\"|\");
for(int i=0; i { this.str=this.str.replace(inj_stra[i], rep_stra[i]); } this.str=this.str.trim(); } } 網(wǎng)頁(yè)jsp程序中調(diào)用此Filter類過(guò)濾不安全字符: <% String value=request.getParameter(\"id\"); if(value==1) value=\"\"; Filter fl=new Filter(); fl.setStr(value); fl.TransactSQLInjection(); value=fl.getStr(); ...... %> 方法TransactSQLInjection的作用是,檢測(cè)通過(guò)網(wǎng)頁(yè)傳遞給服務(wù)器端用于數(shù)據(jù)庫(kù)處理的變量中是否含有不安全字符,例如用“union”聯(lián)合查詢來(lái)執(zhí)行非法的SQL指令,或用“/*”屏蔽后續(xù)的SQL語(yǔ)句等等。當(dāng)發(fā)現(xiàn)這類不安全字符時(shí),將這些字符轉(zhuǎn)換為全角格式,由于數(shù)據(jù)庫(kù)中的關(guān)鍵字不支持全角方式,故轉(zhuǎn)換后這些字符不能再起到破壞作用。 常用的過(guò)濾方式有很多種,比如將不安全字符直接過(guò)濾掉,但直接過(guò)濾仍可能會(huì)存在漏洞,如輸入字段形如“...sselectelect…”,對(duì)不安全字段select過(guò)濾刪除后,剩下的字段正好為“...select…”。而如果對(duì)輸入的不安全字符不進(jìn)行過(guò)濾處理,直接在頁(yè)面上報(bào)錯(cuò),又會(huì)對(duì)正常用戶的錯(cuò)誤操作做出警告,從而影響網(wǎng)站的友好性。所以,將不安全字符替換為全角字符不失為一種可行的策略。 對(duì)于不安全字符的定義范圍,可根據(jù)具體工作環(huán)境靈活掌握。如果是對(duì)網(wǎng)站前臺(tái)進(jìn)行維護(hù),過(guò)濾字符應(yīng)定義的嚴(yán)格而全面,如果是對(duì)網(wǎng)站后臺(tái)進(jìn)行維護(hù),則過(guò)濾標(biāo)準(zhǔn)可相應(yīng)放寬,以方便管理員對(duì)網(wǎng)站的維護(hù)更新。 4.3 其他措施 除了以上針對(duì)程序設(shè)計(jì)方面所做的防范措施外,網(wǎng)絡(luò)工程師在網(wǎng)站的設(shè)計(jì)開(kāi)發(fā)過(guò)程中還應(yīng)該注意一些細(xì)節(jié),從多方面加強(qiáng)網(wǎng)站的整體安全性。比如,對(duì)服務(wù)于普通用戶的頁(yè)面,在與數(shù)據(jù)庫(kù)交互時(shí)盡量不要使用SA、ROOT等高權(quán)限帳戶進(jìn)行連接;不要將異常拋出在客戶端頁(yè)面,因?yàn)殄e(cuò)誤信息經(jīng)常會(huì)暴露一些網(wǎng)站設(shè)計(jì)的關(guān)鍵細(xì)節(jié);不要在數(shù)據(jù)庫(kù)中開(kāi)放某些功能上不必要并且權(quán)限過(guò)大的功能,如Microsoft SQL Server數(shù)據(jù)庫(kù)中的xp_cmdshell等;如使用PHP開(kāi)發(fā)網(wǎng)頁(yè)程序,則應(yīng)該開(kāi)啟PHP魔術(shù)引號(hào)(Magic quote)功能,此功能會(huì)自動(dòng)的將所有從網(wǎng)頁(yè)傳入的參數(shù)中的單引號(hào)字符取代為連續(xù)的2個(gè)單引號(hào)字符;在設(shè)計(jì)數(shù)據(jù)庫(kù)表單名稱及字段名稱時(shí),盡量不要采用容易被猜到的常用名稱,如“admin”、“name”之類的,并且對(duì)于帳戶密碼等關(guān)鍵信息可以進(jìn)行加密處理,比如使用MD5等加密算法,這樣即使攻擊者得到數(shù)據(jù)庫(kù)信息也無(wú)法獲知數(shù)據(jù)的真實(shí)內(nèi)容。 5 結(jié)束語(yǔ) 綜上所述,網(wǎng)站設(shè)計(jì)者應(yīng)該注意提高自身的安全意識(shí),進(jìn)行嚴(yán)密的程序設(shè)計(jì),全面測(cè)試,加強(qiáng)網(wǎng)站的健壯性,并隨時(shí)關(guān)注網(wǎng)站的安全情況,發(fā)現(xiàn)可疑情況立刻采取響應(yīng)措施,做到了這些,SQL注入攻擊是完全可以被避免的。本文基于JSP+Tomcat/6.0.16+MySQL開(kāi)發(fā)的網(wǎng)站提出的SQL注入防護(hù)措施,經(jīng)適當(dāng)修改亦可用于其他開(kāi)發(fā)環(huán)境的Web應(yīng)用中。 參考文獻(xiàn): [1] 劉繁艷,姜瑜.SQL注入攻擊研究[J].中國(guó)科技信息,2005(17). [2] 汪莉莉,鮮明.SQL注入與安全防范技術(shù)分析[J].通信電源技術(shù),2007(5). 石穎(1981-),女,工學(xué)學(xué)士,主要研究方向:信息安全; 孔巧(1981-),女,工學(xué)學(xué)士,主要研究方向:軟件工程。