多想兩分鐘,你可以不用 validateRequest="false"
先來看以下的程式,網頁上放了一個<textarea>及<input type="button">,按鈕後以$.post()方式將<textarea>的內容送至ASP.NET Server端程式,在Page_Load中讀取Request["data"]並顯示出來,另外並透過$.ajaxSetup()指定error錯誤事件函數,捕捉並顯示伺服器端的錯誤資訊。
<%@ Page Language="C#" %>
<!DOCTYPE>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (Request["mode"] == "ajax")
{
Response.ContentType = "text/plain";
Response.Write("Data=" + Request["data"]);
Response.End();
}
}
</script>
<html>
<head runat="server">
<title>MyLab</title>
<style>
body,input { font-size: 9pt; }
</style>
<script src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>
<script>
$(function () {
$("#txtXml").text("<data>Text</data>");
//發生錯誤時顯示錯誤訊息
$.ajaxSetup({
error: function (xhr, textStatus, err) {
alert("ERROR: " + err + "=>" + xhr.responseText);
}
});
//以$.post方式將textarea的內容送至Server端
$("#btnAjaxPost").click(function () {
$.post("?mode=ajax", { data: $("#txtXml").text() },
function (r) { alert(r); }
);
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<textarea id="txtXml" cols="20" rows="4">
</textarea><br />
<input type="button" id="btnAjaxPost" value="Ajax Post" />
</form>
</body>
</html>
老鳥們應該已嗅出異常: 程式會出錯!!

由於我故意在<textarea>中塞入了XML內容,而ASP.NET 2.0起增加了Request Validation的機制,防範惡意人士在URL或PostBack內容中夾雜HTML標籤進行XSS攻擊。很多人遇到這個狀況,直覺反應就是毫不猶豫將@Page validateRequest設成false(甚至一不做二不休,在web.config設定把整個網站的requestValidate都關掉),程式不再出錯,就開開心心快快樂樂繼續寫下去。
身為一個輕度資安偏執者,我反對這種處理方式!! 多想兩分鐘,你可以不要validateRequest="false"。
廚師帶著鍋杓要進總統府外燴,經過金屬探測門安檢時警報大作,解決之道是把金屬探測器拆掉??
不是吧? 針對鍋杓的特例另外處理才是王道,但在資訊系統中無法通過檢核就把防護關掉眼不見為淨的例子比比皆是... (檔案拒絕存取就一律開Everyone、程式在Windows 7跑不了就關UAC)
依我的看法:
應保留requestValidate="true"擋下大部分未知的可能攻擊,針對確實會包含XML/HTML資料且另有內容安全檢核程序的情境,再設法繞道而行
才是安全的策略!
要怎麼讓HTML/XML內容傳送時避免被Request Validation機制阻攔呢? 其實不難,Client端在送出資料前進行編碼,Server端在收到資料進行解碼還原即可。而借用encodeURIComponent()應是最簡單的寫法了,例如:
在$.post()時改成
$.post("?mode=ajax", { data: encodeURIComponent($("#txtXml").text()) }
在Page_Load()時改成
Response.Write("Data=" + HttpUtility.UrlDecode(Request["data"]));
前後端一搭一唱,就可以在不觸發警告的情況下完成XML或HTML資料傳送。但必須牢記,加上了這段設計,意味著駭客就能在Request["data"]中設法摻入HTML標籤,一定要加上邏輯杜絕Request["data"]夾帶HTML標籤引來XSS攻擊的風險。
encodeURIComponent()的做法有個小缺點,就是編碼後內容會膨脹。例如原本的<data>Text</data>會變成%253Cdata%253E%25E5%259C%258B%25E5%25AE%25B6%253C%252Fdata%253E。若要改善,需另覓其他的編碼規則,或在部分情境可考量將XML內容直接當成Post的內容主體(補充參考)。
簡單範例如下,Client端改用$.ajax()方式傳送,指定type="POST", contentType="text/xml"(或是text/plain亦可), 另外還要指定processData=false,即可將data內容當成POST本體,如此亦可避開Request Validation,達成相同目的。
<%@ Page Language="C#" %>
<!DOCTYPE>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (Request["mode"] == "ajax")
{
Response.ContentType = "text/plain";
//使用StreamReader讀入Request.InputStream的所有內容
var sr = new System.IO.StreamReader(Request.InputStream);
Response.Write("Data=" + sr.ReadToEnd());
Response.End();
}
}
</script>
<html>
<head runat="server">
<style>
body,input { font-size: 9pt; }
</style>
<script src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script>
<script>
$(function () {
$("#txtXml").text("<data>Text</data>");
//以$.post方式將textarea的內容送至Server端
$("#btnAjaxPost").click(function () {
$.ajax({
url: "?mode=ajax",
type: "POST",
contentType: "text/xml", //指定格式為XML
data: $("#txtXml").text(),
processData: false, //指定直接傳送內容不做處理
success: function (r) { alert(r); }
});
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<textarea id="txtXml" cols="20" rows="4">
</textarea><br />
<input type="button" id="btnAjaxPost" value="Ajax Post" />
</form>
</body>
</html>