摘要:SQL注入是Web應(yīng)用中常見的一種針對數(shù)據(jù)庫層的攻擊方法。該文分析了SQL注入的原理和攻擊方法,總結(jié)了實(shí)踐中常用的針對SQL注入的防范措施。此防護(hù)措施經(jīng)適當(dāng)修改即可用于多種平臺類型的Web應(yīng)用中。
關(guān)鍵詞:SQL注入;攻擊;防護(hù)措施
中圖分類號:TP393文獻(xiàn)標(biāo)識碼:A文章編號: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ù)庫層的安全漏洞,攻擊者可以利用這個(gè)漏洞在輸入的資料字串中夾帶SQL指令,一旦應(yīng)用程序忽略了檢查,這些夾帶進(jìn)去的指令就會(huì)被數(shù)據(jù)庫服務(wù)器誤認(rèn)為正常的SQL指令而執(zhí)行,從而導(dǎo)致數(shù)據(jù)庫結(jié)構(gòu)以及系統(tǒng)資料外泄,最終使系統(tǒng)遭到破壞。
由于SQL注入是從WWW端口訪問,而且表面看來跟一般的Web頁面訪問沒有區(qū)別,所以多數(shù)防火墻不會(huì)對SQL注入發(fā)出警報(bào)。通過這種攻擊,攻擊者很容易獲得數(shù)據(jù)庫中的賬戶資料、密碼信息等,從而進(jìn)一步篡改系統(tǒng)管理員賬戶,在網(wǎng)頁中加入惡意鏈接以及XSS。經(jīng)由數(shù)據(jù)庫服務(wù)器提供的操作系統(tǒng)支持,攻擊者還能夠修改或控制操作系統(tǒng),破壞硬盤數(shù)據(jù),乃至癱瘓全系統(tǒng)。因此,開發(fā)人員有必要對SQL注入有全面的了解,從而提高自身的安全意識和軟件的健壯性。本文分析了SQL注入的原理和攻擊方法,總結(jié)了一些有效的防護(hù)措施。所有代碼均在JSP+Tomcat/6.0.16+MySQL環(huán)境下運(yùn)行通過。
2 SQL注入的作用原理
很多Web站點(diǎn)都會(huì)利用用戶輸入的參數(shù)動(dòng)態(tài)生成SQL查詢請求。如果攻擊者在URL、表格域或其他的輸入域中輸入自己的SQL命令,而Web程序在組合SQL命令字串時(shí)未進(jìn)行嚴(yán)格的數(shù)據(jù)過濾,就有可能被插入惡意的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ǎng)站的目的。
3 SQL注入攻擊的方法
3.1 確定SQL注入攻擊的注入點(diǎn)
動(dòng)態(tài)網(wǎng)頁在利用傳入的參數(shù)與數(shù)據(jù)庫進(jìn)行存取交互時(shí),如果沒有對傳入的參數(shù)進(jìn)行必要的安全處理,就可能存在SQL注入漏洞。在此,本文模擬了一個(gè)功能為顯示文章信息的網(wǎng)頁,通過傳遞ID號在網(wǎng)頁上顯示某一具體文章的索引號、名稱和作者,本網(wǎng)頁設(shè)計(jì)時(shí)未進(jìn)行任何安全處理措施。在瀏覽器地址欄輸入http://127.0.0.1:8080/article.jsp?id=1,頁面正常顯示:
1|article1|author1
當(dāng)?shù)刂份斎敫臑閔ttp://127.0.0.1:8080/article.jsp?id=1'時(shí),頁面出錯(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.
通過這些錯(cuò)誤提示,攻擊者可獲得許多關(guān)鍵信息,如網(wǎng)站使用的是MySQL數(shù)據(jù)庫,并且使用Tomcat/6.0.16作為應(yīng)用服務(wù)器。
3.2 獲取網(wǎng)站信息
1) 利用SQL語法中的UNION聯(lián)合查詢來猜測當(dāng)前表的字段數(shù)。
由于知道了網(wǎng)站使用的是MySQL數(shù)據(jù)庫,故可利用MySQL的注釋字符“/*”來屏蔽后續(xù)SQL語句,輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1/*
測試表的字段數(shù)是否為1,頁面錯(cuò)誤,返回錯(cuò)誤信息:
Root cause
Javax.servlet.ServletException: java.sql.SQLException:The used SELECT statements have a different number of columns
說明數(shù)據(jù)表的字段數(shù)不為1,不斷增加字段數(shù)進(jìn)行測試,直至輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,1,1 /*
頁面正常顯示,說明當(dāng)前數(shù)據(jù)表的字段數(shù)位3。
2) 使用UNION猜測當(dāng)前數(shù)據(jù)庫中的其他數(shù)據(jù)表。在此我們依據(jù)經(jīng)驗(yàn)嘗試猜測是否有名為admin的表存在。輸入
http://127.0.0.1:8080/article.jsp?id=1' union select 1,1,1 from admin /*
頁面顯示正常,說明數(shù)據(jù)庫中的確存在名為admin的表。
接下來可以猜字段名。比如猜測表中是否含有名為id的字段,可以輸入
http://127.0.0.1:8080/article.jsp?id=1' union select id,1,1 from admin /*
頁面未報(bào)錯(cuò),正常顯示為
1|article1|author1
由此可知,確實(shí)有名為id的字段。攻擊者可使用技巧猜出所有字段名,再將猜出的字段名全部代入union查詢中,當(dāng)字段名正確時(shí)頁面會(huì)返回表內(nèi)信息,則攻擊者可獲得管理員賬號和密碼等重要信息。
3) 使用MySQL函數(shù)顯示或上傳文件。如果當(dāng)前頁面中連接數(shù)據(jù)庫的用戶擁有對文件的讀寫權(quán)限,那么攻擊者可以利用函數(shù)load_file()在頁面中加載系統(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ù)庫服務(wù)器為Windows XP操作系統(tǒng),則系統(tǒng)文件c:/boot.ini內(nèi)的重要信息就會(huì)被暴露到網(wǎng)頁上。
若數(shù)據(jù)庫服務(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)將暴露在頁面上,由此進(jìn)一步分析,攻擊者可得到Web路徑及Web管理地址。
得到了這些重要信息,攻擊者便可以利用outfile等函數(shù)輕松上傳各種木馬病毒,從而達(dá)到控制和攻擊網(wǎng)站的目的。
SQL注入手法多樣,方法靈活,危害巨大,但只要開發(fā)者周密設(shè)計(jì),多方測試,不使網(wǎng)站存在SQL注入漏洞,則完全可以避免此類攻擊。
4 對于SQL注入攻擊的防范措施
一般來說,SQL注入的防范可以從兩方面著手,即服務(wù)器配置和軟件設(shè)計(jì)本身。在此我們主要對程序設(shè)計(jì)方面的防范措施進(jìn)行分析,對服務(wù)器配置方面的防范措施僅略作介紹。
4.1 使用參數(shù)化查詢方法實(shí)現(xiàn)資料存取功能
參數(shù)化查詢是指在構(gòu)造SQL語句時(shí),在需要填入數(shù)據(jù)的地方使用參數(shù)來代替真實(shí)的數(shù)據(jù)信息。此方法可有效防范SQL注入攻擊。
我們?nèi)砸詀rticle.jsp網(wǎng)頁為例,以下為采用參數(shù)化查詢的設(shè)計(jì)方法實(shí)現(xiàn)的jsp網(wǎng)頁代碼。其中,id為從網(wǎng)頁傳入的參數(shù)。
<%@ page import=\"java.util.*\"%>
<%@ page import=\"java.sql.*\" %>
<%
Connection conn=1;
PreparedStatement pstmt=1;//創(chuàng)建PreparedStatement對象
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ù)庫
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語句
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í)例中包含已編譯過的SQL語句,而此SQL語句中可含有若干個(gè)未賦予具體值的IN參數(shù),它們以占位符的形式(如“?”)存在。在SQL語句執(zhí)行前,可通過setXXX的方法為這些參數(shù)傳入具體數(shù)值。也就是說,使用PreparedStatement接口時(shí),數(shù)據(jù)庫服務(wù)器不會(huì)將用戶傳入的參數(shù)內(nèi)容視為SQL指令的一部分來處理,而是在數(shù)據(jù)庫完成SQL指令的編譯后才套用參數(shù)執(zhí)行,因此即使參數(shù)中含有具破壞性的指令,也不會(huì)被數(shù)據(jù)庫執(zhí)行。使用參數(shù)化查詢設(shè)計(jì)是一種可靠的防范SQL注入攻擊的措施。
4.2 對傳入?yún)?shù)中的不安全字符進(jìn)行過濾
為了避免惡意參數(shù)的侵害,還可以采用字符過濾的方法,即對網(wǎng)站前臺傳遞給數(shù)據(jù)庫的參數(shù)進(jìn)行過濾,替換不安全字符。以下代碼將此過濾功能設(shè)計(jì)成javabean,在頁面程序中調(diào)用此javabean實(shí)現(xiàn)過濾功能。
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)頁jsp程序中調(diào)用此Filter類過濾不安全字符: <% String value=request.getParameter(\"id\"); if(value==1) value=\"\"; Filter fl=new Filter(); fl.setStr(value); fl.TransactSQLInjection(); value=fl.getStr(); ...... %> 方法TransactSQLInjection的作用是,檢測通過網(wǎng)頁傳遞給服務(wù)器端用于數(shù)據(jù)庫處理的變量中是否含有不安全字符,例如用“union”聯(lián)合查詢來執(zhí)行非法的SQL指令,或用“/*”屏蔽后續(xù)的SQL語句等等。當(dāng)發(fā)現(xiàn)這類不安全字符時(shí),將這些字符轉(zhuǎn)換為全角格式,由于數(shù)據(jù)庫中的關(guān)鍵字不支持全角方式,故轉(zhuǎn)換后這些字符不能再起到破壞作用。 常用的過濾方式有很多種,比如將不安全字符直接過濾掉,但直接過濾仍可能會(huì)存在漏洞,如輸入字段形如“...sselectelect…”,對不安全字段select過濾刪除后,剩下的字段正好為“...select…”。而如果對輸入的不安全字符不進(jìn)行過濾處理,直接在頁面上報(bào)錯(cuò),又會(huì)對正常用戶的錯(cuò)誤操作做出警告,從而影響網(wǎng)站的友好性。所以,將不安全字符替換為全角字符不失為一種可行的策略。 對于不安全字符的定義范圍,可根據(jù)具體工作環(huán)境靈活掌握。如果是對網(wǎng)站前臺進(jìn)行維護(hù),過濾字符應(yīng)定義的嚴(yán)格而全面,如果是對網(wǎng)站后臺進(jìn)行維護(hù),則過濾標(biāo)準(zhǔn)可相應(yīng)放寬,以方便管理員對網(wǎng)站的維護(hù)更新。 4.3 其他措施 除了以上針對程序設(shè)計(jì)方面所做的防范措施外,網(wǎng)絡(luò)工程師在網(wǎng)站的設(shè)計(jì)開發(fā)過程中還應(yīng)該注意一些細(xì)節(jié),從多方面加強(qiáng)網(wǎng)站的整體安全性。比如,對服務(wù)于普通用戶的頁面,在與數(shù)據(jù)庫交互時(shí)盡量不要使用SA、ROOT等高權(quán)限帳戶進(jìn)行連接;不要將異常拋出在客戶端頁面,因?yàn)殄e(cuò)誤信息經(jīng)常會(huì)暴露一些網(wǎng)站設(shè)計(jì)的關(guān)鍵細(xì)節(jié);不要在數(shù)據(jù)庫中開放某些功能上不必要并且權(quán)限過大的功能,如Microsoft SQL Server數(shù)據(jù)庫中的xp_cmdshell等;如使用PHP開發(fā)網(wǎng)頁程序,則應(yīng)該開啟PHP魔術(shù)引號(Magic quote)功能,此功能會(huì)自動(dòng)的將所有從網(wǎng)頁傳入的參數(shù)中的單引號字符取代為連續(xù)的2個(gè)單引號字符;在設(shè)計(jì)數(shù)據(jù)庫表單名稱及字段名稱時(shí),盡量不要采用容易被猜到的常用名稱,如“admin”、“name”之類的,并且對于帳戶密碼等關(guān)鍵信息可以進(jìn)行加密處理,比如使用MD5等加密算法,這樣即使攻擊者得到數(shù)據(jù)庫信息也無法獲知數(shù)據(jù)的真實(shí)內(nèi)容。 5 結(jié)束語 綜上所述,網(wǎng)站設(shè)計(jì)者應(yīng)該注意提高自身的安全意識,進(jìn)行嚴(yán)密的程序設(shè)計(jì),全面測試,加強(qiáng)網(wǎng)站的健壯性,并隨時(shí)關(guān)注網(wǎng)站的安全情況,發(fā)現(xiàn)可疑情況立刻采取響應(yīng)措施,做到了這些,SQL注入攻擊是完全可以被避免的。本文基于JSP+Tomcat/6.0.16+MySQL開發(fā)的網(wǎng)站提出的SQL注入防護(hù)措施,經(jīng)適當(dāng)修改亦可用于其他開發(fā)環(huán)境的Web應(yīng)用中。 參考文獻(xiàn): [1] 劉繁艷,姜瑜.SQL注入攻擊研究[J].中國科技信息,2005(17). [2] 汪莉莉,鮮明.SQL注入與安全防范技術(shù)分析[J].通信電源技術(shù),2007(5). 石穎(1981-),女,工學(xué)學(xué)士,主要研究方向:信息安全; 孔巧(1981-),女,工學(xué)學(xué)士,主要研究方向:軟件工程。