[ASP.NET]Input Validation and Representation, Data flow

[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 並不認得這種作法Sad smile

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^