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