多想兩分鐘,你可以不用 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>
歡迎推文分享:
Published 01 February 2012 08:21 AM 由 Jeffrey
Filed under: , ,



意見

# lsk said on 31 January, 2012 10:25 PM

雖然知道設validateRequest="false"是不好的

但微軟官方文件也沒有說正確的作法,它也是說設validateRequest="false"就好了,如果msdn有較正確的說法,也不會造成大家都如此做

# Fank Pan said on 03 March, 2012 12:18 AM

MSDN 對於 類別庫 的 介紹從來都只有 說明基本用法 , 進階的設計 本來就是 身為 程式設計師 所需 思考的.

再來  , MSDN 對於 "HttpRequestValidationException" 的說明中有 "我們強列建議您的應用程式應該明確地檢查關於要求中止的所有輸入。" 這句話已經給了 設計思考的方向了......

沒去好好思考這句法 , 就盲目的使用 validateRequest="false" 的你 , 是不是值得檢討......

# taiping8899 said on 08 May, 2012 03:37 AM

"驗證輸入"

"編碼輸出"

是我的體悟

供大家參考~~

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 

搜尋

Go

<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
 
RSS
【工商服務】
OrcsWeb: Windows Server Hosting
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication