ASP.NET中的SQL注入攻擊與防禦

什麼是SQL注入攻擊?html

它是在執行SQL查詢的時候,因爲接收了用戶的非法參數從而致使,所執行的SQL語義與業務邏輯本來所要查詢的語義不相符,從而實現的攻擊。程序員

例如咱們常用的用戶登陸,一般會出現這樣的表單:web

用戶名:________________sql

密   碼:________________數據庫

                                登陸數組

正常狀況下,咱們須要讓用戶填寫他們本身的用戶名和密碼以後,程序會接收用戶輸入的參數 執行查詢操做,而後根據查詢結果,判斷用戶是否可以登陸。安全

可是,若是程序員在編寫SQL查詢操做時候,沒有注意SQL注入問題的話,那麼用戶能夠經過這個表單很輕易的實現一些非正常的訪問,下面咱們據具體的例子來講明。cookie

 

咱們先來佈局一個簡單的表單:ide

 

其中這個表單中包含了用戶名 密碼的輸入文本框,爲了你們便於觀看,密碼我沒有使用password,明文顯示。其中防止注入的複選框是爲了我寫第二段 防止注入的登陸邏輯時,能夠用一個按鈕來表示,而用了單選框是否被選中來進行區別。佈局

 

咱們先來看第一段登陸代碼:

 1     //聲明鏈接
 2     private static OleDbConnection ConnAcc;
 3     protected void Page_Load(object sender, EventArgs e)
 4     {
 5 
 6     }
 7 
 8     /// <summary>
 9     /// 按鈕點擊事件
10     /// </summary>
11     /// <param name="sender"></param>
12     /// <param name="e"></param>
13     protected void Button1_Click(object sender, EventArgs e)
14     {
15         string name = tbx_name.Text;
16         string pwd = tbx_pwd.Text;
17 
18         UserLogin(name, pwd);
19     }
20 
21 
22 
23     /// <summary>
24     /// 第一種登陸驗證操做
25     /// </summary>
26     /// <param name="_loginname"></param>
27     /// <param name="_loginpwd"></param>
28     private void UserLogin(string _loginname,string _loginpwd)
29     {
30         DataSet ds = new DataSet(); //聲明數據集
31         string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //建立查詢語句
32         try
33         {
34 
35             Open(); //打開鏈接
36             OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //建立查詢
37             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users"); //填充數據集
38         }
39         catch (OleDbException exception)
40         {
41             ds = null;
42             Label1.Text = exception.Message; //異常處理
43             return;
44         }
45         finally
46         {
47             Free();
48         }
49         if (ds != null && ds.Tables[0].Rows.Count > 0) //若是數據集中有記錄 表明輸入的用戶名密碼組合正確
50         {
51             Label1.Text = "用戶[" + tbx_name.Text + "]登陸成功!"; //顯示
52 
53             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
54             //保存UID等用戶信息到Session或者cookies中
55             //因爲本案例重點是登陸身份驗證經過環節,所以此處邏輯能夠不繼續寫
56         }
57         else
58         {
59             Label1.Text = "用戶名或密碼錯誤";
60         }
61     }
62 
63 
64 
65 
66     /// <summary>
67     /// 打開數據庫鏈接
68     /// </summary>
69     private static void Open()
70     {
71         ConnAcc = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + HttpContext.Current.Server.MapPath("~/App_Data/db.mdb") + ";");
72         if (ConnAcc.State == ConnectionState.Closed)
73         {
74             ConnAcc.Open();
75         }
76     }
77 
78 
79     /// <summary>
80     /// 關閉鏈接
81     /// </summary>
82     private static void Free()
83     {
84         if (ConnAcc != null)
85         {
86             ConnAcc.Close();
87             ConnAcc.Dispose();
88             ConnAcc = null;
89         }
90     }

 

我在數據庫中添加了一條記錄 用戶名爲admin,密碼爲123456

而後咱們來看一下登陸的實際效果:

OK!登陸成功!看起來一切都很圓滿,可是事實上是這樣嗎?聰明的你必定會想到,若是就此結束了,那本文就毫無心義了,並且這種登陸我剛上學的時候就會了。

沒錯,這只是看上去功能實現了而已,但事實上,這種登陸的判斷邏輯存在着很大的SQL注入風險,下面我就以此段代碼爲例,演示一個簡單的SQL注入的經典例子。

剛纔咱們登陸的時候是輸入的用戶名密碼,密碼若是與用戶名不匹配就沒法登陸成功,那麼當我不知道密碼的狀況下,試試下面的這個組合:

看到這裏你貌似瞬間懂了,而後驚出一身冷汗,而後背後隱隱發涼,爲何不輸入密碼也能登陸成功?那麼我在程度代碼這段加上一個斷點調試一下相比你就明白了。

 

在填充數據集以前,加入斷點,讓程序運行時暫停在這個位置。

因爲我輸入的用戶名爲:admin,密碼並無輸入正確的123456,而是輸入的 ' or '1'='1  這意味這什麼呢?請看下面的變量跟蹤信息:

 

 

這是SQL語句變成了select * from Users Where rg_LoginName='admin' and rg_LoginPwd='' or '1'='1'

咱們查詢本來的意思是:select * from Users Where rg_LoginName=用戶名 and rg_LoginPwd=密碼

而如今的意思就變成了 select * from Users Where rg_LoginName='admin' and rg_LoginPwd=空 or '1'='1'

簡單的說就是至關於用戶經過輸入的密碼中帶有SQL語義的值,變相修改了咱們的查詢語句,在本例中,不管用戶是否知道密碼,他只要在密碼中輸入' or '1'='1',就必定可以登陸成功,由於有了 or '1'='1'的恆成立,因此前面的判斷語句通通失效。這就是很是典型的SQL注入攻擊,經過此種方法,能夠進入後臺,篡改數據等操做。

 

除此之外,包括頁面間傳參,搜索的時候都有可能被多種形式的注入,這是一個至關危險的安全隱患,使你得全部用戶身份驗證形同虛設。

 

那麼如何避免SQL語句的注入呢?

一個最簡單快捷的方法就是過濾單引號,咱們來修改登陸的代碼

 1     /// <summary>
 2     /// 第一種登陸驗證操做
 3     /// </summary>
 4     /// <param name="_loginname"></param>
 5     /// <param name="_loginpwd"></param>
 6     private void UserLogin(string _loginname,string _loginpwd)
 7     {
 8         _loginname = _loginname.Replace("'", ""); //過濾用戶輸入參數中的單引號
 9         _loginpwd = _loginpwd.Replace("'", ""); //過濾用戶輸入參數中的單引號
10 
11         DataSet ds = new DataSet(); //聲明數據集
12         string sql = "select * from Users Where rg_LoginName='" + _loginname + "' and rg_LoginPwd='" + _loginpwd + "'"; //建立查詢語句
13         try
14         {
15 
16             Open(); //打開鏈接
17             OleDbCommand comm = new OleDbCommand(sql, ConnAcc); //建立查詢
18             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users"); //填充數據集
19         }
20         catch (OleDbException exception)
21         {
22             ds = null;
23             Label1.Text = exception.Message; //異常處理
24             return;
25         }
26         finally
27         {
28             Free();
29         }
30         if (ds != null && ds.Tables[0].Rows.Count > 0) //若是數據集中有記錄 表明輸入的用戶名密碼組合正確
31         {
32             Label1.Text = "用戶[" + tbx_name.Text + "]登陸成功!"; //顯示
33 
34             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
35             //保存UID等用戶信息到Session或者cookies中
36             //因爲本案例重點是登陸身份驗證經過環節,所以此處邏輯能夠不繼續寫
37         }
38         else
39         {
40             Label1.Text = "用戶名或密碼錯誤";
41         }
42     }

 

經過把用戶輸入的單引號進行過濾,那麼他在輸入中所帶的SQL語義的值 就不成立了,

咱們繼續使用剛纔的組合:admin,' or '1'='1  來斷點查看下變量:

 

 

這個時候咱們能夠發現,rg_LoginPwd=' or 1=1'  在把用戶輸入的單引號過濾掉以後 用戶輸入的密碼就是 or 1=1(被單引號括起來,被看成字符串處理) 不具備任何的SQL語義了,固然這個密碼是不正確的,所以就不會出現SQL注入的問題了

 

然而利用過濾單引號的方式,只能在必定程度上避免SQL注入的攻擊,卻並不能100%的保證安全,最安全的作法就是使用微軟提供的參數數組的形式進行提交命令。下面咱們再來寫一個登陸的方法。

 1 /// <summary>
 2     /// 第二種登陸驗證操做(參數數組形式)
 3     /// </summary>
 4     /// <param name="_loginname"></param>
 5     /// <param name="_loginpwd"></param>
 6     private void UserLoginSafeMode(string _loginname, string _loginpwd)
 7     {
 8         DataSet ds = new DataSet();
 9         string sql = "select * from Users Where rg_LoginName=@rg_LoginName and rg_LoginPwd=@rg_LoginPwd"; //建立查詢語句
10 
11         //建立參數數組
12         OleDbParameter[] parameters ={
13             new OleDbParameter("@rg_LoginName",OleDbType.VarChar),
14             new OleDbParameter("@rg_LoginPwd",OleDbType.VarChar)
15         };
16 
17         //參數數組賦值
18         parameters[0].Value = _loginname;
19         parameters[1].Value = _loginpwd;
20 
21         try
22         {
23             Open();
24             OleDbCommand comm = new OleDbCommand(sql, ConnAcc);
25             if (parameters != null)
26             {
27                 //向comm中插入參數
28                 foreach (OleDbParameter p in parameters)
29                 {
30                     comm.Parameters.Add(p);
31                 }
32             }
33             new OleDbDataAdapter(comm).Fill(ds, 0, 1, "Users");
34         }
35         catch (OleDbException exception)
36         {
37             ds = null;
38             Label1.Text = exception.Message; //異常處理
39             return;
40         }
41         finally
42         {
43             Free();
44         }
45         if (ds != null && ds.Tables[0].Rows.Count > 0) //若是數據集中有記錄 表明輸入的用戶名密碼組合正確
46         {
47             Label1.Text = "用戶[" + tbx_name.Text + "]登陸成功!"; //顯示
48 
49             //int uid = int.Parse(ds.Tables[0].Rows[0]["rg_ID"].ToString());
50             //保存UID等用戶信息到Session或者cookies中
51             //因爲本案例重點是登陸身份驗證經過環節,所以此處邏輯能夠不繼續寫
52         }
53         else
54         {
55             Label1.Text = "用戶名或密碼錯誤";
56         }
57     }

 

你必定會細心的注意到,與第一個登陸方法不一樣的地方時,在聲明瞭sql語句以後,又聲明瞭一個參數數組,而且賦值,在comm中遍歷插入。經過這樣的方式,咱們無需過濾單引號,參數數組會把那些帶有語義的SQL語句看成字符串值來處理,從而絕對的從根本上避免了SQL注入攻擊

 

 

OK,關於SQL注入攻擊與防範就說到這裏,關於此類話題還有不少不少,不過咱們只要知道使用參數數組的方式提交查詢命名就能夠從根本上避免此類問題的發生,具體的其餘關於SQL注入的問題,感興趣的同窗能夠自行在網上查找資料,有什麼疑問歡迎留言

 

本文的DEMO下載:http://files.cnblogs.com/webconfig/SQL_Injection_Attack_DEMO.rar

 

本文出自 低調碼農的筆記簿 http://www.cnblogs.com/webconfig/p/3622498.html 轉載請註明出處,若有謬誤不當之處,歡迎指正拍磚,不勝感謝!!

相關文章
相關標籤/搜索