.NET防SQL注入方法

SQL語句

利用SqlCommand傳參數的方法:

string strSQL="SELECT * FROM [user] WHERE user_id=@id";
SqlCommand cmd = new SqlCommand();
cmd.CommandText = strSQL;
cmd.Parameters.Add("@id",SqlDbType.VarChar,20).Value=Request["id"].ToString();

過濾禁止運行法:

          /// <summary>
          /// 過濾SQL語句,防止注入
          /// </summary>
          /// <param name="strSql"></param>
          /// <returns>0 - 沒有注入, 1 - 有注入 </returns>
          public int filterSql(string sSql)
          {
              int srcLen, decLen = 0;
              sSql = sSql.ToLower().Trim();
              srcLen = sSql.Length;
              sSql = sSql.Replace("exec", "");
              sSql = sSql.Replace("delete", "");
              sSql = sSql.Replace("master", "");
              sSql = sSql.Replace("truncate", "");
              sSql = sSql.Replace("declare", "");
              sSql = sSql.Replace("create", "");
              sSql = sSql.Replace("xp_", "no");
              decLen = sSql.Length;
              if (srcLen == decLen) return 0; else return 1;        
          }

存儲過程

因爲在存儲過程中就可以設置變量的類型,所以也無需對數據做任何操作

漏洞演示:
http://xxx.xxx.xxx.xxx/xxx.asp?id=17;DROP TABLE D99_Tmp;CREATE TABLE D99_Tmp(subdirectory VARCHAR(100),depth VARCHAR(100),[file] VARCHAR(100))

禁止後使用SQL 語句中就不能出現“exec, master, delete, truncate, declare, create, xp_”這些字符。

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

SQL注入攻擊是非常令人討厭的安全漏洞,是所有的web開發人員,不管是什麼平臺,技術,還是數據層,需要確信他們理解和防止的東西。不幸的是,開發人員往往不集中花點時間在這上面,以至他們的應用,更糟糕的是,他們的客戶極其容易受到攻擊。

Michael Sutton 最近發表了一篇非常發人深省的帖子,講述在公共網上這問題是多麼地普遍。他用Google的Search API建了一個C#的客戶端程序,尋找那些易受SQL 注入攻擊的網站。其步驟很簡單:

尋找那些帶查詢字符串的網站(例如,查詢那些在URL裏帶有 "id=" 的URL)
給這些確定爲動態的網站發送一個請求,改變其中的id=語句,帶一個額外的單引號,來試圖取消其中的SQL語句(例如,如 id=6' )
分析返回的回覆,在其中查找象"SQL" 和"query"這樣的詞,這往往表示應用返回了詳細的錯誤消息(這本身也是很糟糕的)
檢查錯誤消息是否表示發送到SQL服務器的參數沒有被正確加碼(encoded),如果如此,那麼表示可對該網站進行SQL注入攻擊
對通過Google搜尋找到的1000個網站的隨機取樣測試,他檢測到其中的11.3%有易受SQL注入攻擊的可能。這非常,非常地可怕。這意味着黑客可 以遠程利用那些應用裏的數據,獲取任何沒有hashed或加密的密碼或信用卡數據,甚至有以管理員身份登陸進這些應用的可能。這不僅對開發網站的開發人員 來說很糟糕,而且對使用網站的消費者或用戶來說更糟糕,因爲他們給網站提供了數據,想着網站是安全的呢。

那麼SQL注入攻擊到底是什麼玩意?

有幾種情形使得SQL注入攻擊成爲可能。最常見的原因是,你動態地構造了SQL語句,卻沒有使用正確地加了碼(encoded)的參數。譬如,考慮 這個SQL查詢的編碼,其目的是根據由查詢字符串提供的社會保險號碼(social security number)來查詢作者(Authors):


Dim SSN as String
Dim SqlQuery as String

SSN = Request.QueryString("SSN")
SqlQuery = "SELECT au_lname, au_fname FROM authors WHERE au_id = '" + SSN + "'"

如果你有象上面這個片斷一樣的SQL編碼,那麼你的整個數據庫和應用可以遠程地被黑掉。怎麼會呢?在普通情形下,用戶會使用一個社會保險號碼來訪問這個網站,編碼是象這樣執行的:


' URL to the page containing the above code
http://mysite.com/listauthordetails.aspx?SSN=172-32-9999

' SQL Query executed against the database
SELECT au_lname, au_fname FROM authors WHERE au_id = '172-32-9999'

這是開發人員預期的做法,通過社會保險號碼來查詢數據庫中作者的信息。但因爲參數值沒有被正確地加碼,黑客可以很容易地修改查詢字符串的值,在要執行的值後面嵌入附加的SQL語句 。譬如,


' URL to the page containing the above code
http://mysite.com/listauthordetails.aspx?SSN=172-32-9999';DROP DATABASE pubs --

' SQL Query executed against the database
SELECT au_lname, au_fname FROM authors WHERE au_id = '';DROP DATABASE pubs --
注意到沒有,我可以在SSN查詢字符串值的後面添加" ';DROP DATABASE pubs -- ",通過 ";"字符來終止當前的SQL語句,然後添加了我自己的惡意的SQL語句,然後把語句的其他部分用"--"字符串註釋掉。因爲我們是手工在編碼裏構造 SQL語句,我們最後把這個字符串傳給了數據庫,數據庫會先對authors表進行查詢,然後把我們的pubs數據庫刪除。"砰(bang)"的一聲,數 據庫就沒了!

萬一你認爲匿名黑客刪除你的數據庫的結果很壞,但不幸的是,實際上,這在SQL注入攻擊所涉及的情形中算是比較好的。一個黑客可以不單純摧毀數據, 而是使用上面這個編碼的弱點,執行一個JOIN語句,來獲取你數據庫裏的所有數據,顯示在頁面上,允許他們獲取用戶名,密碼,信用卡號碼等等。他們也可以 添加 UPDATE/INSERT 語句改變產品的價格,添加新的管理員賬號,真的搞砸你(screw up your life)呢。想象一下,到月底檢查庫存時,發現你庫房裏的實際產品數與你的帳目系統(accounting system)彙報的數目有所不同。。。

那該如何保護你自己?

SQL注入攻擊是你需要擔心的事情,不管你用什麼web編程技術,再說所有的web框架都需要擔心這個的。你需要遵循幾條非常基本的規則:

1) 在構造動態SQL語句時,一定要使用類安全(type-safe)的參數加碼機制。大多數的數據API,包括ADO和ADO.net,有這樣的支持,允許 你指定所提供的參數的確切類型(譬如,字符串,整數,日期等),可以保證這些參數被恰當地escaped/encoded了,來避免黑客利用它們。一定要 從始到終地使用這些特性。

例如,在ADO.net裏對動態SQL,你可以象下面這樣重寫上述的語句,使之安全:

Dim SSN as String = Request.QueryString("SSN")

Dim cmd As new SqlCommand("SELECT au_lname, au_fname FROM authors WHERE au_id = @au_id")
Dim param = new SqlParameter("au_id", SqlDbType.VarChar)
param.Value = SSN
cmd.Parameters.Add(param)
這將防止有人試圖偷偷注入另外的SQL表達式(因爲ADO.net知道對au_id的字符串值進行加碼),以及避免其他數據問題(譬如不正確地轉換數值類 型等)。注意,VS 2005內置的TableAdapter/DataSet設計器自動使用這個機制,ASP.net 2.0數據源控件也是如此。

一個常見的錯誤知覺(misperception)是,假如你使用了存儲過程或ORM,你就完全不受SQL注入攻擊之害了。這是不正確的,你還是需要確定在給存儲過程傳遞數據時你很謹慎,或在用ORM來定製一個查詢時,你的做法是安全的。

2) 在部署你的應用前,始終要做安全審評(security review)。建立一個正式的安全過程(formal security process),在每次你做更新時,對所有的編碼做審評。後面一點特別重要。很多次我聽說開發隊伍在正式上線(going live)前會做很詳細的安全審評,然後在幾周或幾個月之後他們做一些很小的更新時,他們會跳過安全審評這關,推說,"就是一個小小的更新,我們以後再做 編碼審評好了"。請始終堅持做安全審評。

3) 千萬別把敏感性數據在數據庫裏以明文存放。我個人的意見是,密碼應該總是在單向(one-way )hashed過後再存放,我甚至不喜歡將它們在加密後存放。在默認設置下,ASP.net 2.0 Membership API 自動爲你這麼做,還同時實現了安全的SALT 隨機化行爲(SALT randomization behavior)。如果你決定建立自己的成員數據庫,我建議你查看一下我們在這裏發表的我們自己的Membership provider的源碼。同時也確定對你的數據庫裏的信用卡和其他的私有數據進行了加密。這樣即使你的數據庫被人入侵(compromised)了的話, 起碼你的客戶的私有數據不會被人利用。

4) 確認你編寫了自動化的單元測試,來特別校驗你的數據訪問層和應用程序不受SQL注入攻擊。這麼做是非常重要的,有助於捕捉住(catch)"就是一個小小 的更新,所有不會有安全問題"的情形帶來的疏忽,來提供額外的安全層以避免偶然地引進壞的安全缺陷到你的應用裏去。

5) 鎖定你的數據庫的安全,只給訪問數據庫的web應用功能所需的最低的權限。如果web應用不需要訪問某些表,那麼確認它沒有訪問這些表的權限。如果web 應用只需要只讀的權限從你的account payables表來生成報表,那麼確認你禁止它對此表的 insert/update/delete 的權限。

SQL注入式攻擊

SQL注入式攻擊是利用是指利用設計上的漏洞,在目標服務器上運行Sql命令以及進行其他方式的攻擊動態生成Sql命令時沒有對用戶輸入的數據進行驗證是Sql注入攻擊得逞的主要原因。

比如:
如果你的查詢語句是select * from admin where username="&user&" and password="&pwd&""

那麼,如果我的用戶名是:1 or 1=1

那麼,你的查詢語句將會變成:

select * from admin where username=1 or 1=1 and password="&pwd&""

這樣你的查詢語句就通過了,從而就可以進入你的管理界面。

所以防範的時候需要對用戶的輸入進行檢查。特別式一些特殊字符,比如單引號,雙引號,分號,逗號,冒號,連接號等進行轉換或者過濾。

需要過濾的特殊字符及字符串有:

net user
xp_cmdshell
/add
exec master.dbo.xp_cmdshell
net localgroup administrators
select
count
Asc
char
mid

:
"
insert
delete from
drop table
update
truncate
from
%

下面關於解決注入式攻擊的防範代碼,供大家學習參考!

js版的防範SQL注入式攻擊代碼:

  
<script language="javascript">
<!--
var url = location.search;
var re=/^/?(.*)(select%20|insert%20|delete%20from%20|count/(|drop%20table|update%20truncate%20|asc/(|mid/(|char/(|xp_cmdshell|exec%20master|net%20localgroup%20administrators|/"|:|net%20user|/|%20or%20)(.*)$/gi;
var e = re.test(url);
if(e) {
    alert("地址中含有非法字符~");
    location.href="error.asp";
}
//-->
<script>


asp版的防範SQL注入式攻擊代碼~:


[CODE START]
<%
On Error Resume Next
Dim strTemp

If LCase(Request.ServerVariables("HTTPS")) = "off" Then
strTemp = "http://"
Else
strTemp = "https://"
End If

strTemp = strTemp & Request.ServerVariables("SERVER_NAME")
If Request.ServerVariables("SERVER_PORT") <> 80 Then strTemp = strTemp & ":" & Request.ServerVariables("SERVER_PORT")

strTemp = strTemp & Request.ServerVariables("URL")
If Trim(Request.QueryString) <> "" Then strTemp = strTemp & "?" & Trim(Request.QueryString)

strTemp = LCase(strTemp)
If Instr(strTemp,"select%20") or Instr(strTemp,"insert%20") or Instr(strTemp,"delete%20from") or Instr(strTemp,"count(") or Instr(strTemp,"drop%20table") or Instr(strTemp,"update%20") or Instr(strTemp,"truncate%20") or Instr(strTemp,"asc(") or Instr(strTemp,"mid(") or Instr(strTemp,"char(") or Instr(strTemp,"xp_cmdshell") or Instr(strTemp,"exec%20master") or Instr(strTemp,"net%20localgroup%20administrators") or Instr(strTemp,":") or Instr(strTemp,"net%20user") or Instr(strTemp,"") or Instr(strTemp,"%20or%20") then
Response.Write "<script language=javascript>"
Response.Write "alert(非法地址!!);"
Response.Write "location.href=error.asp;"
Response.Write "<script>"
End If
%>
[CODE END]

C# 檢查字符串,防SQL注入攻擊
這個例子裏暫定爲=號和號
bool CheckParams(params object[] args)
{
     string[] Lawlesses={"=",""};
     if(Lawlesses==null||Lawlesses.Length<=0)return true;
     //構造正則表達式,例:Lawlesses是=號和號,則正則表達式爲 .*[=}].* (正則表達式相關內容請見MSDN)
     //另外,由於我是想做通用而且容易修改的函數,所以多了一步由字符數組到正則表達式,實際使用中,直接寫正則表達式亦可;


     string str_Regex=".*[";
     for(int i=0;i< Lawlesses.Length-1;i++)
         str_Regex+=Lawlesses[i]+"|";
     str_Regex+=Lawlesses[Lawlesses.Length-1]+"].*";
     //
     foreach(object arg in args)
     {
         if(arg is string)//如果是字符串,直接檢查
         {
             if(Regex.Matches(arg.ToString(),str_Regex).Count>0)
             return false;
         }
         else if(arg is ICollection)//如果是一個集合,則檢查集合內元素是否字符串,是字符串,就進行檢查
         {
             foreach(object obj in (ICollection)arg)
             {
                 if(obj is string)
                 {
                     if(Regex.Matches(obj.ToString(),str_Regex).Count>0)
                     return false;
                 }
             }
         }
     }
     return true;


以下是較爲簡單的防範方法,這些都是大家比較熟悉的方法,我就是轉帖過來。希望能給你一點幫助~

主要是針對數字型的變量傳遞:

id = Request.QueryString("id")

If Not(isNumeric(id)) Then

    Response.Write "非法地址~"

    Response.End

End If

以下是正常顯示代碼~


如何再編碼的過程中防範SQL注入式攻擊

1、什麼是SQL注入式攻擊我 們知道Microsoft的SQL Server數據庫是支持一次數據庫查詢執行多條SQL語句的,比如說再查詢管理器裏面,我們可以輸入一條SQL語句
select * from table1 select * from table2
假如所選擇的數據庫中確實存在table1和table2的話,那麼這個SQL語句就能夠執行成功,並且能夠返回正確的結果。同時,我們知道現在很多系統 或者發佈在Internet上的網站,管理員爲了方便,一般都是直接採用sa的身份來連接數據庫的,那麼SQL注入式攻擊主要就是抓住了系統在這麼兩個方 面的弱點,攻擊者把SQL語句插入到web表單輸入,或者頁面的查詢字符串中,從而欺騙服務器執行惡意的SQL命令,以達到攻擊的目的。

2.   注入式攻擊的詳細解釋SQL下面我們將以一個簡單的用戶登陸爲例,結合代碼詳細解釋一下SQL注入式攻擊,與及他的防範措施。對於一個簡單的用戶登陸可能的代碼如下:
try
{
string strUserName = this.txtUserName.Text;
string strPwd = this.txtPwd.Text;
string strSql = "select * from userinfo where UserName='" + strUserName + "' and Password='" + strPwd + "'";
SqlConnection objDbConn = new SqlConnection("數據庫連接字符串");
SqlDataAdapter objAdapter = new SqlDataAdapter(strSql,objDbConn);
DataSet objDataSet = null;
objAdapter.Fill(objDataSet);//TODO 對獲取的數據進行判斷。
}
catch (System.Exception e)
{
this.lblMsg.Text = e.Message;
this.lblMsg.Visible = true;
}
在上面這段代碼中,如果用戶的輸入是正常的用戶名和密碼的話,那麼執行都會比較正常,但是,假如輸入用戶名的時候,輸入的是“johny’--”的 話,在 SQLServer裏面執行的語句將會是“select * from userinfo where UserName=’johny’--‘ and Password=’密碼’”,只要數據庫中存在johny這個用戶的話,那麼不管密碼是什麼,語句都能夠執行成功,並且能夠順利通過登陸。還 有更加厲害的,我們知道SQLServer裏面有一些系統的存儲過程,能夠執行操作系統的很多命令,比如xp_cmdshell,假如上面用戶登陸的時 候,用戶名部分輸入的是“johny’ exec xp_cmdshell ‘format d:/s’--”,大家想想一下後果是什麼?有惡意的用戶,只要把’format d:/s’這個命令稍加改造就能夠做很多不合法的事情啦。

3. 如何防範SQL注入式攻擊既然我們解釋了爲什麼會產生SQL的注入式攻擊,那麼我們接下來說說如何來防範這種情況的發生,解決的辦法有很多,我們逐個介紹一下。

a) 在進行系統部署的時候,不要採用sa來連接數據庫,在數據庫中新建一個帳號,對這個帳號的權限進行限制,只允許在指定的數據庫裏面執行一下添加,修改的權限,這樣我們就可以很好地防止通過執行xp_cmdshell來攻擊服務器了。

b) 採用用存儲過程來執行所有的查詢。SQL參數的傳遞方式將防止攻擊者利用單引號和連字符實施攻擊。此外,它還使得數據庫權限可以限制到只允許特定的存儲過 程執行,所有的用戶輸入必須遵從被調用的存儲過程的安全上下文,這樣就很難再發生注入式攻擊了。在這裏大家可能會說,我只是要執行一個簡單的查詢,難道也 要寫成一個存儲過程嗎,這樣是不是太麻煩了,其實沒有關係,我們只要採用ado.net裏面的SqlParameter把上面的代碼稍加改造,如下
try
{
string strUserName = this.txtUserName.Text;
string strPwd = this.txtPwd.Text;
string strSql = "select * from userinfo where UserName=@UserName and Password=@Password";
SqlConnection objDbConn = new SqlConnection("數據庫連接字符串");
SqlDataAdapter objAdapter = new SqlDataAdapter(strSql,objDbConn);
objAdapter.SelectCommand.Parameters.Add("@UserName",strUserName);
objAdapter.SelectCommand.Parameters.Add("@Password",strPwd);
DataSet objDataSet = null;
objAdapter.Fill(objDataSet); //TODO 對獲取的數據進行判斷。
}
catch (System.Exception e)
{
this.lblMsg.Text = e.Message;
this.lblMsg.Visible = true;
}
上面的這段代碼,我們就可以比較好地防範SQL 注入式攻擊了,原因是SQLServer裏面存在一個sp_executesql的系統存儲過程,ado裏面只要sql語句中用到了 SqlParameter的方式的話,那麼sql語句將會通過sp_executesql來執行,具體的情況大家可以用SQL Server裏面的Profile來進行跟蹤一下,就非常清楚了。通過這種方法,我們就即可以不用寫存儲過程,有能夠很好地防範SQL注入式攻擊了對用戶 的輸入進行校驗,將用戶輸入裏面存在的單引號進行轉義(一個單引號替換成兩個單引號),並且過濾裏面存在的註釋符,與及特殊命令。

轉載

發佈了75 篇原創文章 · 獲贊 5 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章