.NET 3.5裡多了些新玩意,看過保哥的超完美組合:LinqDataSource + ListView + DataPager + jQuery及Rick Strahl的ListView and DataPager in ASP.NET 3.5兩篇介紹ListView的文章,ListView對前端有極佳主控權的特色深得我心,我打算逐步在未來的專案中用ListView取代DataGrid或GridView。

不過我有注意到大部分文章在介紹Templated Control時都省略了資安風險的提醒(也許是假設這是讀者的基本常識),擔心有些朋友看完這類Sample就直接拿來用在正式系統中,在此特地嘮叨兩句。

例如以下的例子,它示範如何用Eval(fileName)的寫法,將結果顯示在ItemTemplate中。

<%@ Page Language="C#" AutoEventWireup="true" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server"><title>Eval XSS Test</title>
<script type="text/C#" runat="server">
protected void Page_Load(object sender, EventArgs e)
{
    DateTime d = DateTime.Today;
    var ds = new[]
    {
        new { SN=1, EntryDate = d.AddDays(-15), Name = "Jeffrey", Remark = "Verified"},
        new { SN=2, EntryDate = d.AddDays(-7), Name = "Linda", Remark = "NA"},
        new { SN=3, EntryDate = d.AddDays(-2), Name = "Sharon", Remark = ""}
    };
    ListView1.DataSource = ds;
    ListView1.DataBind();        
}
</script></head><body>
    <form id="form1" runat="server">
    <asp:ListView ID="ListView1" runat="server">
    <LayoutTemplate>
        <table border=1>
        <thead><tr><th>Sn</th><th>EntryDate</th><th>Name</th><th>Remark</th></tr></thead>
        <tbody runat="server" id="itemPlaceholder"></tbody>
        </table>
    </LayoutTemplate>
    <ItemTemplate>
        <tr><td><%#Eval("SN")%></td><td><%#Eval("EntryDate", "{0:yyyy/MM/dd}") %></td>
        <td><%#Eval("Name", "")%>
        </td><td><asp:Label ID="lblRemark" runat="server" Text='<%#Eval("Remark", "")%>'>
        </asp:Label></td>
        </tr>
    </ItemTemplate>
    </asp:ListView>
    </form>
</body></html>

註: Eval("Remark", "")的寫法可以防止資料為null時發生Exception,跟之前保哥提過的Convert.ToString()用意差不多,而Eval("EntryDate", "{0:yyyy/MM/dd}") 則示範了Eval的格式化功能。

以上的程式碼可以順利執行沒有問題,卻潛在XSS的風險。

在此例中,我們用自建匿名類別陣列的方式製作出DataSource,但實務上DataSource常是由資料庫查詢取得的資料,而資料來源又常來自於使用者(或駭客)的輸入,若輸入時未能確實檢核,或是遭人篡改(例如前陣子很熱門的游擊式的SQL Injection攻擊),就有可能夾帶XSS攻擊的內容。用以上的例子,我們來模擬資料有毒的狀況,如下:

    var ds = new[]    
    {        
        new { SN=1, EntryDate = d.AddDays(-15), Name = "Jeffrey", Remark = "Verified"},
        new { SN=2, EntryDate = d.AddDays(-7), Name = "Linda", Remark = "NA"},
        new { SN=3, EntryDate = d.AddDays(-2), Name = "Sharon", Remark = ""},
        new { SN=4, EntryDate = d, Name = "Hacker<script>alert('Hacked!');<" + "/script>", 
                Remark = "XSS<script>alert('XSS!!');<" + "/script>" }
     }; 

網頁閃出兩個Javascript alert,代表駭客已在你的網頁搶灘成功,可以開始為非作歹了。而且不管是直接插入欄位文字(如Name)或是設成Label.Text(如Remark),一樣都會中鏢。(順便提一下,Label1.Text = myDataRow["userInput"].ToString()這類的寫法也是不安全的)

要如何因應? 其實不難! 不可能或不該出現HTML Code的地方,記得加上HttpUtility.HtmlEncode轉換。除了防止XSS,萬一有使用者在內容中輸了</td>之類描述HTML Tag的文字,也不會產生干擾,讓你的HTML內容大亂。
(分享一個頗經典的例子,測試員在系統Bug資料庫的問題主旨欄位填寫了"Some.aspx有多餘的</td></tr></table>",結果整個Bug清單Table當場腰斬,Bug少一半,讓RD好高興!)

     <td><%#HttpUtility.HtmlEncode(Eval("Name", ""))%></td>
     <td><asp:Label ID="lblRemark" runat="server" Text='<%#HttpUtility.HtmlEncode(Eval("Remark", ""))%>'>
     </asp:Label></td>

小小動作,永保安康。各位勞苦功高的開發人員,不要忘了隨手做資安哦!

【延伸閱讀】ASP.NET防駭指南你的網站在裸奔嗎?游擊式的SQL Injection攻擊


Comments

# by digi

server.HtmlEncode

# by Jeffrey

to digi, Server.HtmlEncode背後呼叫了HttpUtility.HtmlEncode,二者作用相同。HttpUtility的好處是不限aspx.cs,在一般的Class中也可使用。

Post a comment