T-SQL篇如何防止SQL注入的解決方法
所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請(qǐng)求的查詢字符串,欺騙服務(wù)器執(zhí)行惡意的SQL命令,對(duì)于這種行為,我們應(yīng)該如何制止呢?本文將介紹一種方法,希望可以幫助有需要的朋友。
1.什么是SQL注入
所謂SQL注入式攻擊,就是攻擊者把SQL命令插入到Web表單的輸入域或頁面請(qǐng)求的查詢字符串,欺騙服務(wù)器執(zhí)行惡意的SQL命令。在某些表單中,用戶輸入的內(nèi)容直接用來構(gòu)造(或者影響)動(dòng)態(tài)SQL命令,或作為存儲(chǔ)過程的輸入?yún)?shù),這類表單特別容易受到SQL注入式攻擊。
2.怎么進(jìn)行SQL注入
關(guān)于怎么進(jìn)行SQL注入,網(wǎng)上已經(jīng)有很多文章詳細(xì)介紹過了,可以參考博友滴答的雨的博文 《SQL注入攻防入門詳解》,親測有效。當(dāng)執(zhí)行完文中的5、6、7三步的時(shí)候,你會(huì)發(fā)現(xiàn)服務(wù)器上的安全保護(hù)措施都已是浮云,服務(wù)器也因此變成了名副其實(shí)的“肉機(jī)”。下面附上一張我在本機(jī)執(zhí)行完文中描述的腳本后的效果截圖(Win8 x64 操作系統(tǒng)):
微軟的“不禁止即允許(Not forbidden is allow)”的做法使得操作系統(tǒng)像是服務(wù)器所穿的鏤空禮物一樣,美觀但卻有很多“漏洞”。好了,現(xiàn)在此小黑已經(jīng)擁有了服務(wù)器的管理員權(quán)限,很顯然元芳怎么看已經(jīng)不重要了。
3.如何防止SQL注入的發(fā)生
滴答的雨已經(jīng)在博文詳細(xì)闡述了SQL Server數(shù)據(jù)庫如何進(jìn)行防注入的操作,這里不再贅述。這一篇我主要說一下對(duì)于一個(gè)使用拼接SQL進(jìn)行查詢操作的Web應(yīng)用,怎么進(jìn)行防注入操作。
先說一些前提,為什么我們要使用拼接SQL的方式進(jìn)行查詢?偷懶唄。這在開發(fā)過程中,看似省去了編寫參數(shù)化部分的代碼量,節(jié)省了時(shí)間和精力。但這樣做的結(jié)果就是應(yīng)用的安全性大打折扣,而且拼SQL方式創(chuàng)建的應(yīng)用,后期的維護(hù)難度也很大。SQL參數(shù)化查詢是最簡單有效的避免SQL注入的解決方案,目前主流的ORM框架(MyBatis.NET/NHibernate/EntityFramework)都內(nèi)置支持并且推薦使用這種方式進(jìn)行持久層封裝。
然而有數(shù)據(jù)庫不支持參數(shù)化查詢?cè)趺崔k?是的,你沒有看錯(cuò),確實(shí)有這樣的數(shù)據(jù)庫存在。吐個(gè)槽先,個(gè)人認(rèn)為,一切不支持參數(shù)化查詢的數(shù)據(jù)庫都是在“耍流氓”,這種天然的缺陷會(huì)讓小黑們肆無忌憚地去“非禮”服務(wù)器,至少是數(shù)據(jù)庫本身。在這樣的情況下,我覺得其他功能做得再好也只能算是花拳繡腿,連最基本的數(shù)據(jù)都保護(hù)不了,那不等同于將勞動(dòng)成果拱手讓人。按照存在即合理的邏輯,我們暫且認(rèn)為它是合理的。#p#
來說說我目前的做法,基于上述數(shù)據(jù)庫創(chuàng)建的Web應(yīng)用,拼接SQL操作已經(jīng)滲透到站點(diǎn)的每個(gè)頁面、每個(gè)用戶控件,所以我采用的方式是請(qǐng)求過濾。
下面是防SQL注入的操作類:
- /// <summary>
- ///SqlInject 的摘要說明
- /// </summary>
- public class SqlInject : System.Web.UI.Page
- {
- //檢測到注入后的處理方式: 0:僅警告;1:警告+記錄;2:警告+自定義錯(cuò)誤頁面;3:警告+記錄+自定義錯(cuò)誤頁面
- private const int _type = 0;
- private const string errRedirectPage = "/err.aspx";
- //如果記錄注入信息,那么請(qǐng)?jiān)O(shè)置:errMDBpath:數(shù)據(jù)庫路徑
- private const string errMDBpath = "/SqlInject.mdb";
- //過濾特征字符
- //過濾特征字符
- private static string StrKeyWord = ConfigurationManager.AppSettings["SqlKeyWord"]; //@"select|insert|delete|from|count(|drop table|update|truncate|asc(|mid(|char(|xp_cmdshell|exec|master|net local group administrators|net user|or|and";
- private static string StrRegex = ConfigurationManager.AppSettings["SqlRegex"]; //@";|/|(|)|[|]|{|}|%|@|*|'|!"; // 原始過濾條件:【-|;|,|/|(|)|[|]|{|}|%|@|*|'|!】
- private HttpRequest request;
- public SqlInject(System.Web.HttpRequest _request)
- {
- this.request = _request;
- }
- ///<summary>
- ///檢測SQL注入及記錄、顯示出錯(cuò)信息
- ///</summary>
- public void CheckSqlInject()
- {
- bool isInject = false;
- if (CheckRequestQuery() || CheckRequestForm())
- {
- isInject = true;
- }
- else
- {
- return;
- }
- switch (_type)
- {
- case 0:
- ShowErr();
- break;
- case 1:
- ShowErr();
- SaveToMdb();
- break;
- case 2:
- ShowErr();
- string temp;
- System.Web.HttpContext.Current.Response.Write("<script>setTimeout(\"" + "location.href='" + errRedirectPage + "'" + "\",5000)</script>");
- break;
- case 3:
- ShowErr();
- SaveToMdb();
- System.Web.HttpContext.Current.Response.Write("<script>setTimeout(\"" + "location.href='" + errRedirectPage + "'" + "\",5000)</script>");
- break;
- default:
- break;
- }
- System.Web.HttpContext.Current.Response.End();
- }
- private void SaveToMdb()
- {
- OleDbConnection conn = new OleDbConnection("Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + Server.MapPath(errMDBpath));
- conn.Open();
- OleDbCommand cmd = conn.CreateCommand();
- cmd.CommandText = "insert into [Record] (sIP,sDate,sPath) values ('" +
- request.ServerVariables["REMOTE_ADDR"].ToString() + "','" +
- DateTime.Now + "','" + request.ServerVariables["URL"].ToLower() + RelaceSingleQuotes(request.QueryString.ToString()) + "')";
- int code = cmd.ExecuteNonQuery();
- if (code == 1)
- 75: System.Web.HttpContext.Current.Response.Write("<br>****以上信息已記錄至日志數(shù)據(jù)庫****");
- else
- System.Web.HttpContext.Current.Response.Write("<br>日志數(shù)據(jù)庫出錯(cuò)");
- conn.Close();
- }
- private string RelaceSingleQuotes(string _url)
- {
- string URL = _url.Replace("'", "單引號(hào)");
- return URL;
- }
- private void ShowErr()
- {
- //string msg = @"<font color=red>請(qǐng)不要嘗試未授權(quán)之入侵檢測!</font>" + @"<br><br>";
- //msg += @"操作IP:" + request.ServerVariables["REMOTE_ADDR"] + @"<br>";
- //msg += @"操作時(shí)間:" + DateTime.Now + @"<br>";
- //msg += @"頁面:" + request.ServerVariables["URL"].ToLower() + request.QueryString.ToString() + @"<br>";
- //msg += @"<a href='#' onclick='javascript:window.close()'>關(guān)閉</a>";
- //System.Web.HttpContext.Current.Response.Clear();
- //System.Web.HttpContext.Current.Response.Write(msg);
- System.Web.HttpContext.Current.Response.Write("<script>alert('請(qǐng)不要嘗試未授權(quán)之入侵檢測!');javascript:history.go(-1);</script>");
- }
- ///<summary>
- /// 特征字符
- ///</summary>
- public static string KeyWord
- {
- get
- {
- return StrKeyWord;
- }
- }
- ///<summary>
- /// 特征符號(hào)
- ///</summary>
- public static string RegexString
- {
- get
- {
- return StrRegex;
- }
- }
- ///<summary>
- ///檢查字符串中是否包含Sql注入關(guān)鍵字
- /// <param name="_key">被檢查的字符串</param>
- /// <returns>如果包含注入true;否則返回false</returns>
- ///</summary>
- private static bool CheckKeyWord(string _key)
- {
- string[] pattenString = StrKeyWord.Split('|');
- string[] pattenRegex = StrRegex.Split('|');
- foreach (string sqlParam in pattenString)
- {
- if (_key.Contains(sqlParam + " ") || _key.Contains(" " + sqlParam))
- {
- return true;
- }
- }
- foreach (string sqlParam in pattenRegex)
- {
- if (_key.Contains(sqlParam))
- {
- return true;
- }
- }
- return false;
- }
- ///<summary>
- ///檢查URL中是否包含Sql注入
- /// <param name="_request">當(dāng)前HttpRequest對(duì)象</param>
- /// <returns>如果包含注入true;否則返回false</returns>
- ///</summary>
- public bool CheckRequestQuery()
- {
- if (request.QueryString.Count > 0)
- {
- foreach (string sqlParam in this.request.QueryString)
- {
- if (sqlParam == "__VIEWSTATE") continue;
- if (sqlParam == "__EVENTVALIDATION") continue;
- if (CheckKeyWord(request.QueryString[sqlParam].ToLower()))
- {
- return true;
- }
- }
- }
- return false;
- }
- ///<summary>
- ///檢查提交的表單中是否包含Sql注入
- /// <param name="_request">當(dāng)前HttpRequest對(duì)象</param>
- /// <returns>如果包含注入true;否則返回false</returns>
- ///</summary>
- public bool CheckRequestForm()
- {
- if (request.Form.Count > 0)
- {
- foreach (string sqlParam in this.request.Form)
- {
- if (sqlParam == "__VIEWSTATE") continue;
- if (sqlParam == "__EVENTVALIDATION") continue;
- if (CheckKeyWord(request.Form[sqlParam]))
- {
- return true;
- }
- }
- }
- return false;
- }
- }
#p#
過濾類是在某前輩的作品基礎(chǔ)上改的,很抱歉我已經(jīng)找不到最原始的出處了。需要在Web.Config中添加防SQL注入的特征字符集:
復(fù)制代碼 代碼如下:
- <!--防SQL注入時(shí)的特征字符集-->
- <add key="SqlKeyWord" value="select|insert|delete|from|count(|drop table|update|truncate|asc(|mid(|char(|xp_cmdshell|exec|master|net local group administrators|net user|or|and"/>
- <add key="SqlRegex" value=";|(|)|[|]|{|}|%|@|*|'|!"/>
使用方法很簡單,在站點(diǎn)的Global文件中,添加 Application_BeginRequest 事件即可:
復(fù)制代碼 代碼如下:
- protected void Application_BeginRequest(object sender, EventArgs e)
- {
- //防SQL注入代碼
- SqlInject myCheck = new SqlInject(this.Request);
- myCheck.CheckSqlInject();
- }
ASP.NET SQL 注入免費(fèi)解決方案
任何一種使用數(shù)據(jù)庫web程序(當(dāng)然,也包括桌面程序)都有被SQL注入的風(fēng)險(xiǎn)。防止被SQL注入,最基本的方法是在代碼級(jí)別就要阻止這種可能,這個(gè)網(wǎng)上講的很多,我就不多說了。不過如果你拿到的是一個(gè)已經(jīng)完工的產(chǎn)品,這個(gè)時(shí)候該如何解決呢?我介紹幾種對(duì)于ASP和ASP.NET有效的防止SQL注入的方案,而且是免費(fèi)的。
UrlScan 3.1
UrlScan 3.1是一個(gè)安全方面的工具,微軟官方的東西。它會(huì)檢查所有IIS處理的HTTP請(qǐng)求。UrlScan 可以在有安全問題的HTTP請(qǐng)求到達(dá)應(yīng)用程序之前就阻止這個(gè)請(qǐng)求。UrlScan 3.1 是UrlScan 2.5的一個(gè)升級(jí)版本,支持Windows Vista 和Windows Server 2008系統(tǒng)之上的IIS 5.1, IIS 6.0 和 IIS 7.0。
鏈接地址:http://www.iis.net/expand/UrlScan 這里還有很多非常有用的IIS擴(kuò)展,可以看看。
IIS 6 SQL Injection Sanitation ISAPI Wildcard
這個(gè)ISAPI dll 也是通過檢查HTTP請(qǐng)求避免SQL注入。只兼容windows 2003上的 IIS 6.0。對(duì)于Windows XP 上的 IIS 5 不支持。