[ASP.NET]Input Validation and Representation, Data flow
最近ASP.NET 1.1程式被客戶用 Fortify 掃出有 Cross-Site Scripting: Persistent(Input Validation and Representation, Data flow) 的嚴重缺失。
主要是因為在DataGrid中使用範本欄位,而範本中Label的Text直接取自DataSource的欄位資料,沒有再經過HtmlEncode,如下Label1的Text,
<asp:DataGrid id="DataGrid1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="c2" FooterText="c">
<ItemTemplate>
<asp:Label id="Label1" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.c2") %>'>Label</asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
網路上主要的解決方式就是在DataBinder.Eval前再加入HtmlEncode,如下,
<asp:DataGrid id="Datagrid2" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="c2" FooterText="c">
<ItemTemplate>
<asp:Label id="Label2" runat="server" Text='<%# System.Web.HttpUtility.HtmlEncode(DataBinder.Eval(Container, "DataItem.c2")) %>' />
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
但是因為程式還蠻多支的,要一個一個去Replace還蠻花心力的,所以也可在DataGrid的ItemCreated事件中,將字串的資料加入HtmlEncode的處理,如下,
Private Sub Datagrid3_ItemCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles Datagrid3.ItemCreated
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim dt As DataTable = Datagrid3.DataSource
For Each dc As DataColumn In dt.Columns
If dc.DataType Is GetType(String) Then
Dim row As DataRow = CType(e.Item.DataItem, DataRowView).Row
row(dc.ColumnName) = Server.HtmlEncode(row(dc.ColumnName))
End If
Next
End If
End Sub
以上只是驗証可從底層ItemCreated事件中去調整row(dc.ColumnName)的值,進而避免掉「Input Validation and Representation, Data flow 」這個真正的問題。
以下為ASP.NET 1.1的DataGrid範例程式,
aspx
<asp:DataGrid id="DataGrid1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="c2" FooterText="c">
<ItemTemplate>
<asp:Label id="Label1" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.c2") %>'>Label</asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<hr>
<asp:DataGrid id="Datagrid2" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="c2" FooterText="c">
<ItemTemplate>
<asp:Label id="Label2" runat="server" Text='<%# System.Web.HttpUtility.HtmlEncode(DataBinder.Eval(Container, "DataItem.c2")) %>'>Label</asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<hr>
<asp:DataGrid id="Datagrid3" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateColumn HeaderText="c2" FooterText="c">
<ItemTemplate>
<asp:Label id="Label3" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.c2") %>'>Label</asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
aspx.vb
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim dt As DataTable = New DataTable("mydata")
dt.Columns.Add("c1", GetType(String))
dt.Columns.Add("c2", GetType(String))
dt.Rows.Add(New String() {"c1", "c21"})
dt.Rows.Add(New String() {"c12", "<script language='javascript'>alert(""xxx"");</script>"})
DataGrid1.DataSource = dt
DataGrid1.DataBind()
Datagrid2.DataSource = dt
Datagrid2.DataBind()
Datagrid3.DataSource = dt
Datagrid3.DataBind()
End Sub
Private Sub Datagrid3_ItemCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles Datagrid3.ItemCreated
If e.Item.ItemType = ListItemType.Item OrElse e.Item.ItemType = ListItemType.AlternatingItem Then
Dim dt As DataTable = Datagrid3.DataSource
For Each dc As DataColumn In dt.Columns
If dc.DataType Is GetType(String) Then
Dim row As DataRow = CType(e.Item.DataItem, DataRowView).Row
row(dc.ColumnName) = Server.HtmlEncode(row(dc.ColumnName))
End If
Next
End If
End Sub
以下為ASP.NET 4.0的GridView範例程式,
aspx
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="c1" HeaderText="c1" />
<asp:TemplateField HeaderText="c2">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.c2") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<hr />
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="c1" HeaderText="c1" />
<asp:TemplateField HeaderText="c2">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# System.Web.HttpUtility.HtmlEncode(DataBinder.Eval(Container, "DataItem.c2")) %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<hr />
<asp:GridView ID="GridView3" runat="server" AutoGenerateColumns="False"
onprerender="GridView3_PreRender" onrowdatabound="GridView3_RowDataBound"
onrowupdating="GridView3_RowUpdating" onrowcreated="GridView3_RowCreated">
<Columns>
<asp:BoundField DataField="c1" HeaderText="c1" />
<asp:TemplateField HeaderText="c2">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("c2") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
DataTable mydata = new DataTable("mydata");
mydata.Columns.Add("c1", typeof(string));
mydata.Columns.Add("c2", typeof(string));
mydata.Rows.Add("c1", "c2");
mydata.Rows.Add("c1", @"<script language='javascript'>alert(""xxx"");</script>");
GridView1.DataSource = mydata;
GridView1.DataBind();
GridView2.DataSource = mydata;
GridView2.DataBind();
GridView3.DataSource = mydata;
GridView3.DataBind();
}
protected void GridView3_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataTable dt = GridView3.DataSource as DataTable;
foreach (DataColumn dc in dt.Columns)
{
if (dc.DataType == typeof(string))
{
DataRow row = ((DataRowView)e.Row.DataItem).Row;
row[dc.ColumnName] = Server.HtmlEncode(row[dc.ColumnName] as string);
}
}
}
}
結論
雖然透過DataGrid的ItemCreated事件,或是GridView的RowCreated事件可處理HtmlEncode的問題。
但重點是 Fortify 並不認識這種解決方式,所以想要讓 Fortify 可以掃過,還是要在aspx上加入HtmlEncode去處理。
不然就要跟客戶說明,在事件中一樣可以解決這種問題。
會想到在Grid的事件中處理,是因為我們系統中有設計不同的樣版程式,所以,如果就可以從這些樣版程式來調整。
只可惜,Fortify 並不認得這種作法。
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^