應用範圍:
ASP.NET vertion 1.1
ASP.NET vertion 2.0
概要:
文本主要介紹如何校驗使用者輸入進而防止注入式攻擊.校驗使用者輸入是非常必要的,幾乎所有程式級的攻擊都包含惡意輸入的手段.
你應該校驗包括字段,查詢字串參數,Cookie等一切使用者輸入項來保護你的程式免受注入攻擊.你得假設所有的使用者輸入都是惡意的,確定在伺服器端對所有的使用者輸入進行校驗.使用基于用戶端的驗證可以減少頁面的住返次數,改進性能,改善使用者體驗,但是不要僅僅依賴于此,因為用戶端的驗證很容易就可以被黑客騙過去.
為了驗證輸入内容,你應該為每一個輸入字段定義可接受的輸入規則.比較好的作法是從輸入字段的長度,範圍,格式,類型來作限制.使用可接受的字元限制清單而不是非法字元清單來限制輸入.使用非法字元清單限制的方式不好,是因為你幾乎不可能過濾所有的有害輸入.
如果你需要接受HTML字元輸入,最好使用HtmlEncode之類的方法将它進行編碼確定安全再将它們顯示出來.
内容:
目的
總括
分步實施提要
第一步. 使用ASP.NET 請求校驗.
第二步. 使用權用限制輸入.
第三步. 對不安全的輸入進行編碼.
第四步. 對Sql語句使用指令參數方式.
第五步. 驗證ASP.NET的錯誤沒有被傳回用戶端.
額外的資源
目的:
對輸入的字串長度,範圍,格式和類型進行限制.
在開發ASP.NET程式時使用請求驗證防止注入攻擊.
使用ASP.NET驗證控件進行輸入驗證.
對不安全的輸出編碼.
使用指令參數集模式防止注入攻擊.
防止錯誤的詳細資訊被傳回到用戶端.
概述 :
你應該在程式中驗證所有的不信任輸入.你應該假定所有的使用者輸入都是非法的.使用者可以在應用程式中提供表單字段,查詢字串,用戶端cookies和浏覽器環境值比如使用者代理字串和IP位址等.
弱輸入校驗通常為注入攻擊提供了機會.下面是常見的利用弱輸入校驗或無輸入校驗進行攻擊的手段.
SQL 注入(SQL injection). 如果你使用使用者的輸入值來動态構造SQL語句,那麼資料庫可能執行攻擊性的有害SQL語句.
跨站腳本(Cross-site scripting). 跨站腳本攻擊利用網頁驗證漏洞注入用戶端腳本.接下來這些代碼被發送到受信任的用戶端電腦上并被浏覽器解釋執行.因為這些代碼來自受信任的站點,是以浏覽器無法得知這些代碼是有害的.
未授權的檔案通路(Unauthorized file access).如果你的代碼從調用者那裡接受輸入,惡意使用者可以看到你對檔案的操作過程進而通路那些受保護的檔案或者使用你的代碼注入非法資料.
注意 : 注入攻擊可通過使用HTTP或HTTPS Secure Socket Layer(SSL) 連接配接. 傳輸加密技術不能用來防禦攻擊.
通常的輸入驗證方法總結如下.你應在所有的需要通過網絡輸入的地方進行驗證,比如文本框和其它表單輸入字段, 查詢字串參數,cookies,伺服器端變量和網絡方法參數.注意,過濾政策應該是隻允許正确的輸入然後拒絕非法輸入.這是因為定義正确的輸入政策比過濾所有的非法輸入要容易,那通常很難包括所有的非法輸入.
通入如下幾個方面驗證輸入内容:
限制.驗證是否輸入的是正确的類型,字元長度,格式和範圍.可以應用ASP.NET驗證控件來限制伺服器控件輸入.限制其它來源的輸入可以使用正規表達式和自定義的驗證規則.
拒絕.檢測已知的有害資料輸入并拒絕.
過濾.有時候你會希望過濾掉使用者輸入中那些有安全隐患的那些部分.例如,你的程式允許自由格式的輸入,比如備注字段,你會允許特定的安全HTML标記象<b>,<i>及其它的HTML标記.
步驟提要
通過以下步驟保護你的ASP.NET程式不受注入式攻擊危害 :
第一步.使用ASP.NET請求驗證.
第二步.限制輸入.
第三步.對不安全的輸出進行編碼.
第四步.對SQL查詢語句使用指令參數.
第五步.驗證ASP.NET的出錯資訊沒有洩漏至用戶端.
下面的章節将對這些步驟進行詳細讨論.
預設地,ASP.NET 1.1和2.0請求驗證會對送至伺服器的資料檢測是否含有HTML标記元素和保留字元.這可以防止使用者向程式中輸入腳本.請求驗證會對照一個有潛在威脅的字元串清單進行比對,如果發現異常它會抛出一個HttpRequestValidationException類型的異常.
你可以在你的web.config檔案中的<pages>元素中加入validateRequest="false" 或在單獨的頁面的@Pages元素裡面設定ValidateRequest = "false"來禁用此項功能.
如果你想禁用請求驗證功能,你可以僅在需要的頁面禁用它.比如你在程式頁面上包含一個可接受HTML格式輸入的字段.
确定在Machine.config檔案中請求驗證功能被打開.
請求驗證功能在ASP.NET中被預設啟用.你可以在Machine.config.comments檔案中看到如下的預設設定.
<pages validateRequest = "true" ... />
确認你沒有修改你的伺服器的Machine.config和應用程式的Web.config檔案裡的預設設定.
測試ASP.NET請求驗證
你可以測試請求驗證的作用.建立一個ASP.NET頁面通過設定ValidateRequest = "fasle"禁用請求驗證,代碼如下 :

<%@ Language="C#" ValidateRequest="false" %>

<html>

<script runat="server">

void btnSubmit_Click(Object sender, EventArgs e)
{
// If ValidateRequest is false, then 'hello' is displayed
// If ValidateRequest is true, then ASP.NET returns an exception
Response.Write(txtString.Text);
}

</script>

<body>

<form id="form1" runat="server">

<asp:TextBox id="txtString" runat="server"

Text="<script>alert('hello');</script>" />

<asp:Button id="btnSubmit" runat="server" OnClick="btnSubmit_Click"

Text="Submit" />

</form>

</body>

</html>
當你運作頁面的時候,"Hello"被顯示在一個消息框中,因為在txtString中的腳本被執行并被用戶端的浏覽器處理.
如果你設定ValidateRequest = "true" 或者移除ValidateRequest頁面屬性,ASP.NET請求驗證會拒絕腳本輸入并抛出一個象下面這樣的錯誤資訊.

A potentially dangerous Request.Form value was detected from the client (txtString="<script>alert('hello
").
注意 不要僅僅依賴請求驗證功能,而隻是把它作為自定驗證的輔導手段.
第二步.限制輸入
要限制輸入通過如下方法 :
使用伺服器端的輸入驗證.不要依賴于用戶端的驗證,因為它很容易就被繞過.使用用戶端驗證是為了減少頁面返住次數提升性能,改進使用者體驗.
驗證輸入的長度,範圍,格式和類型.確定輸入内容是符合要求的正确内容.
使用強資料類型.為數字類型的輸入指定如Integer或者Double的類型.為字元輸入指定為String資料類型.為日期時間輸入指定DateTime類型.
要驗證表單裡面的HTML控件輸入字段,在伺服器端代碼中進行驗證,使用Regex正規表達式類型可以幫助限制字元輸入.下面的章節介紹如何限制普通輸入類型的變量.
驗證字元串字段
要驗證字元串字段,如姓名,位址,傳真,生份證号碼,使用正規表達式.
限制可接受的字元範圍.
啟動格式規則.例如,基于模式的字段如稅号,郵編,郵遞區号需要規定的字元模式.
驗證長度.
使用正規表達式驗證控件(RegularExpresionValidator)
要使用則表達式驗證控件需要設定待驗證的控件名(ControlToValidate),驗證表達式(ValidationExpression)和出錯提示(ErrorMessage).相關的屬性設定請看下面的代碼示例.

<form id="WebForm" method="post" runat="server">

<asp:TextBox id="txtName" runat="server"></asp:TextBox>

<asp:RegularExpressionValidator id="nameRegex" runat="server"

ControlToValidate="txtName"

ValidationExpression="^[a-zA-Z'.\s]{1,40}$"

ErrorMessage="Invalid name">

</asp:regularexpressionvalidator>

</form>

在上面的代碼中,正規表達式被用于限定輸入的名字為字母(允許大寫字母和小寫字母),空格,單名省略号象O'Dell和句點.此外,輸入的字元長度被限定在40個字元.
注意 正規表達式驗證控件(RegularExpressionValidator)會自動加入脫字元(^)和美元符号($)作為開始和結束的分隔符.如果你沒有在自定義的表達式中加入他們那麼最好加入.加入分隔符隻是為了讓你的表達式得到想要的那部分資料内容.
使用正規表達式類(Regex Class)
如果你沒有使用伺服器端的控件(意味着你不能使用驗證控件),或者你需要其它的輸入字段源而非表單字段(比如查詢字串參數和cookies),那麼你可以使用正規表達式類(Regex class).
使用正規表達式類
加入使用using字首的語句導入System.Text.RegularExpressions命名空間.
确認正規表達式包含"^"和"$"(字串開始處,字串結束處).
調用Regex類的IsMatch方法,下面是代碼示例.

// Instance method:

Regex reg = new Regex(@"^[a-zA-Z'.\s]{1,40}$");

Response.Write(reg.IsMatch(txtName.Text));


// Static method:

if (!Regex.IsMatch(txtName.Text,@"^[a-zA-Z'.\s]{1,40}$"))
{
// Name does not match expression
}

如果你不能把經常使用的正規表達式緩存起來,你應該使用IsMatch靜态方法來改進性能防止不必要的對象建立過程.
驗證數字字段
在大多數情況下,應該驗證數字的輸入和範圍.使用伺服器控件驗證數字字段的輸入和範圍,使用RangeValidator控件.RangeValidator支援貨币,日期,整型,雙精度和字元串類型的資料.
使用RangeValidator控件需要設定需要驗證的控件名(ControlToValidate),類型(Type),最小值(MinimumValue),最大值(MaximumValue),和出錯提示資訊(ErrorMessage)屬性.下面是代碼示例 :

<asp:RangeValidator

ID="RangeValidator1"

Runat="server"

ErrorMessage="Invalid range. Number must be between 0 and 255."

ControlToValidate="rangeInput"

MaximumValue="255"

MinimumValue="0" Type="Integer" />
如果你沒使用伺服器控件,你可以将輸入值轉化成整型再進行驗證來完成對數字的範圍驗證.例如,要驗證一個整數是否合法,使用ASP.NET2.0提供的新方法Int32.TryParse将輸入值轉化為System.Int32的變量類型.這個方法會在轉換失敗時傳回false.

Int32 i;

if (Int32.TryParse(txtInput.Text, out i) == false)
// Conversion failed

如果你使用早先的ASP.NET版本,可以在try/catch語句塊中 使用Int32.Parse或者Convert.ToInt32方法并可以在轉換失敗時處理抛出的FormatException錯誤.
下面的示例代碼示範了如何驗證來自HTML文本框的整數類型的類型和範圍.

<%@ Page Language="C#" %>


<script runat="server">


void Page_Load(object sender, EventArgs e)
if (Request.RequestType == "POST")
{
int i;
if (Int32.TryParse(Request.Form["integerTxt"], out i) == true)
{
// TryParse returns true if the conversion succeeds
if ((0 <= i && i <= 255) == true)
{
Response.Write("Input data is valid.");
}
else
Response.Write("Input data is out of range");
}
else
Response.Write("Input data is not an integer");
}


</script>



<body>

<form id="form1" action="NumericInput.aspx" method="post">

<div>

Enter an integer between 0 and 255:

<input name="integerTxt" type="text" />

<input name="Submit" type="submit" value="submit" />

</div>

</form>

</body>


驗證日期字段
你需要驗證日期字段是否是正确的類型.在大多數情況下,你也需要驗證它們的範圍,如驗證它們是否是将來或是過去的時間.如果你使用伺服器控件來捕獲一個日期輸入值,同時你希望這個值在一個特定的範圍内,你可以使用範圍驗證控件(RangeValidator)并設定它允許的類型為Date類型.這個控件允許你指定一個特殊的時間段通過設定起始的時刻.如果你需要以今天的時間作為參照來驗證,比如驗證一個時間是在将來還是過去,你可以使用CustomValidator驗證控件.
使用CustomValidator控件來驗證一個日期需要設定ControlToValidate和ErrorMessage屬性,在OnServerValidate事件中指定一個自定義的驗證邏輯方法.下面是示例代碼.





void ValidateDateInFuture(object source, ServerValidateEventArgs args)
{
DateTime dt;
// Check for valid date and that the date is in the future
if ((DateTime.TryParse(args.Value, out dt) == false) ||
(dt <= DateTime.Today))
{
args.IsValid = false;
}
}






<form id="form1" runat="server">


<asp:Label ID="Label1" Runat="server"

Text="Future Date:"></asp:Label>

<asp:TextBox ID="futureDatetxt" Runat="server"></asp:TextBox>

<asp:CustomValidator

ID="CustomValidator1" Runat="server"

ErrorMessage="Invalid date. Enter a date in the future."

ControlToValidate="futureDatetxt"

OnServerValidate="ValidateDateInFuture">

</asp:CustomValidator>

<br />

<asp:Button ID="submitBtn" Runat="server" Text="Submit" />




注意 上面的代碼使用的方法DateTime.TryParse是ASP.NET2.0提供的新方法.
過濾自由文本字段
過濾輸入,你需要使不安全的輸入不被當作代碼來對待.例如,你的程式使使用者不能讀取共享資料庫内的資料,你首先需要過濾資料使它們在輸出的時候沒有危險.使用HttpUtility.HtmlEncode方法先對輸入值進行編碼.
允許有限的輸入HTML代碼
在@ Page頁面元素内加以下字段ValidateRequest = "false"禁用ASP.NET請求驗證
使用HtmlEncode方法對輸入的字元串進行編碼
使用StringBuilder對象,調用它的Replace方法對字元中的HTML進行替換
下面的代碼給出了這種辦法的示例.此頁面設定ValidateRequest = "fasle"禁用了ASP.NET請求驗證.它的HTML編碼為了顯示簡單的文本格式允許使用<b>和<i>标記.

<%@ Page Language="C#" ValidateRequest="false"%>




void submitBtn_Click(object sender, EventArgs e)
// Encode the string input
StringBuilder sb = new StringBuilder(
HttpUtility.HtmlEncode(htmlInputTxt.Text));
// Selectively allow and <i>
sb.Replace("&lt;b&gt;", "<b>");
sb.Replace("&lt;/b&gt;", "");
sb.Replace("&lt;i&gt;", "<i>");
sb.Replace("&lt;/i&gt;", "");
Response.Write(sb.ToString());







<asp:TextBox ID="htmlInputTxt" Runat="server"

TextMode="MultiLine" Width="318px"

Height="168px"></asp:TextBox>

<asp:Button ID="submitBtn" Runat="server"

Text="Submit" OnClick="submitBtn_Click" />




驗證查詢字串的值
驗證查詢字串的長度,範圍,格式和類型.通常,你使用一個合并的正規表達式來完成以下任務:
限制輸入值
設定明确的範圍檢查條件
指定輸入的類型并将它轉換成ASP.NET平台下的類型,處理任何由類型轉換引發的異常下面的代碼示例示範了使用Regex類驗證由查詢字串傳遞過來的名字字元串

void Page_Load(object sender, EventArgs e)
if (!System.Text.RegularExpressions.Regex.IsMatch(
Request.QueryString["Name"], @"^[a-zA-Z'.\s]{1,40}$"))
Response.Write("Invalid name parameter");
else
Response.Write("Name is " + Request.QueryString["Name"]);

驗證Cookie值
象查詢字串這樣被儲存在Cookie裡面的值很容易被使用者修改.同樣地驗證這些值的長度,範圍,格式和類型.
驗證檔案和URL位址
如果你的程式允許輸入檔案名,檔案位址或者檔案存放路徑,你需要驗證它們的格式是否正确并且根據你的程式實際情況它指向一個有效的位置.如果此步驗證失敗,你的程式可能會被錯誤地要求通路檔案.
驗證檔案路徑
為了避免你的程式被使用者利用來通路檔案,防止接受使用者編寫代碼輸入的檔案或者檔案路徑.例如 :
如果你接受輸入檔案名,使用System.IO.Path.GetFileName方法來取得檔案的全稱
如果你不得不接受輸入檔案路徑,使用System.IO.Path.GetFullPath來取得完整的檔案路徑
使用MapPath方法防止跨應用程式的映射
如果你使用MapPath方法在伺服器上映射一個提供的虛拟目錄到一個實體目錄,使用Request.MapPath方法的一個帶bool參數的重載版本來防止跨應用程式的映射.下面是此項技術的示例代碼 :

try
string mappedPath = Request.MapPath( inputPath.Text,
Request.ApplicationPath, false);

catch (HttpException)
// Cross-application mapping attempted

最終的false參數将會防止跨應用程式的映射.這意味着使用者不允許使用".."這樣的文法提供一個不在你所指定的虛拟目錄裡面的非法路徑.
如果你使用伺服器控件,你可以使用Control.MapPathSecure方法擷取虛拟目錄對應的實際目錄位址.
Control.MapPathSecure方法在通路一個非授權的檔案時抛出一個HttpException的異常.需要更多資訊,請參看.NET Framework文檔中的Control.MapPathSecure方法介紹.
使用代碼通路安全機制限制檔案輸入輸出
管理者可以通過設定程式使它的可信度為"中"來限制程式向它所在的虛拟目錄讀寫檔案的能力..NET代碼安全機制可以保證程式在它所在的虛拟目錄之外沒有任何的檔案通路權利.
要設定一個應用程式的信任度為"中",可以在Web.config或者Machine.config檔案中加入:
<trust level = "Medium" />
驗證URL
你可以用象下面的這樣的正規表達式來對URL進行特征比對.
^(?:http|https|ftp)://[a-zA-Z0-9\.\-]+(?:\:\d{1,5})?(?:[A-Za-z0-9\.\;\:\@\&\=\+\$\,\?/]|%u[0-9A-Fa-f]{4}|%[0-9A-Fa-f]{2})*$
這隻是限制輸入的格式,不驗證它是否在應用程式可接受的範圍内.你應該驗證它是否在你的程式的上下文中有效.例如,您的應用程式是否跟你指定的伺服器進行通訊?
第三步.對不安全代碼進行編碼
如果您輸入文本輸入到一個網頁,使用HttpUtility.HtmlEncode方法對它進行編碼.如果這些文來自于使用者輸入,資料庫或者一個本地檔案,請確定總是這樣做.
同樣地,如果您書寫的URL裡面包含不安全的字元因為他們來自于使用者輸入内容,資料庫等,使用HttpUtility.UrlEncode方法進行編碼.
為了防止存儲資料前編碼可能會使存儲的資料受到破壞,請確定在将它們顯示出來時盡可能後面的步驟将它們編碼.
使用HtmlEncode對不安全的輸出編碼
HtmlEncode對HTML标記置換成特殊含文的字元串來表示這些符号而又讓浏覽器不把它們當作HTML标記來解釋處理.比如."<"被置換成&lt; " (冒号) 被替換成&quot; 這些标記被顯示成無害的文本.
<%@ Page Language="C#" ValidateRequest="false" %>

Response.Write(HttpUtility.HtmlEncode(inputTxt.Text));



<html xmlns="http://www.w3.org/1999/xhtml" >




<asp:TextBox ID="inputTxt" Runat="server"

TextMode="MultiLine" Width="382px" Height="152px">

</asp:TextBox>

<asp:Button ID="submitBtn" Runat="server" Text="Submit"

OnClick="submitBtn_Click" />




檢視HTML編碼的效果,請建立一個虛拟目錄将前述的檔案放進去,運作此頁面,在文本框中輸入一些HTML代碼,點選送出按鈕.例如,下面的輸入被當作普通文本來顯示.
Run script and say hello <script>alert('hello');</script>
如果你移除調用HtmlEncode方法,簡單地輸入文本的内容,浏覽器會執行代碼并彈出一個提示框.
使用UrlEncode 方法對不安全的URL位址進行編碼
如果你需要擷取有使用者輸入部分的URL參數,這可能帶來一定的安全風險,使用HttpUtility.UrlEncode方法對這個位址字元串編碼.
HttpUtility.UrlEncode(urlString);
第四步.對SQL語句使用指令參數方式.
為了避免注入式攻擊請使用SQL的參數方式.參數(Parameters)集合提供類型檢測和長度檢測.如果你使用參數集合,輸入的内容将被當作文本值來對待,資料庫不會執行包含在其中的代碼.使用參數集方式的一個額外的好處是,你可以嚴格限定輸入的類型和長度.如果輸入型超出範圍将會觸發異常.
當調用一個存儲過程時使用參數集
下面的代碼片段示範了在調用存儲過程時使用參數集的例子.

SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin",

myConnection);

myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;

SqlParameter parm = myCommand.SelectCommand.Parameters.Add(

"@LoginId", SqlDbType.VarChar, 11);

parm.Value = Login.Text;

在建立你自己的SQL語句時使用參數集.
如果你不能使用存儲過程,你仍然可以使用參數集,請看下面的代碼.

SqlDataAdapter myCommand = new SqlDataAdapter(

"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", myConnection);

SQLParameter parm = myCommand.SelectCommand.Parameters.Add(

"@au_id" ,SqlDbType.VarChar, 11);

Parm.Value = Login.Text;

第五步.驗證ASP.NET的錯誤資訊沒有被傳回到用戶端
你可以使用<customErrors>元素來配置用戶端,一般的錯誤資訊應該被程式錯誤檢測機制傳回到用戶端.
請确認已經更改web.config中的mode屬性為"remoteOnly",下面是示例.
<customErrors mode = "remoteOnly">
安在裝了一個ASP.NET 的程式之後,你可以按照如下設定指定用戶端的錯誤資訊頁面。
<customErrors mode = "on" defaultRedirect = "YourErrorPage.htm">
本文轉自tiasys部落格園部落格,原文連結:http://www.cnblogs.com/tiasys/archive/2012/05/21/2511676.html,如需轉載請自行聯系原作者