.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>
小小動作,永保安康。各位勞苦功高的開發人員,不要忘了隨手做資安哦!