利用 HttpModule,基于输出,统一控制、干预、处理(例如: 过滤关键字、AntiXSS) ASP.Net WebForm Control 展现属性的方案原型


/*
利用 HttpModule 统一干预、处理(例如: 过滤关键字) ASP.Net WebForm Control 的输出渲染
前几天在阅读老赵的《一个较完整的关键字过滤解决方案》上中下,
http://www.cnblogs.com/JeffreyZhao/archive/2008/12/22/filter-forbidden-word-solution.html
当时提出了一个在 HttpModule "输出流"中转"字符串"后利用"替换",实现"基于输出干预处理"的过滤方案,
《HttpModule 实现 ASP.Net (*.aspx) 中文简繁体的自动转换,不用修改原有的任何代码,直接部署即可!》
http://microshaoft.cnblogs.com/archive/2005/12/03/289665.html
我也承认那个方案是有性能问题的。
最近在想一个实现全局解决"跨站脚本攻击"的统一通用干预处理,实现安全输出的方案。
"跨站脚本攻击"的一般解决方案就是在"输出"时,也就是先做"HTML/JavaScript Encode",然后输出 Safe Html/JavaScript。
ASP.Net WebControl 及其属性,有很多其实都不会自动 Encode 后输出的,即不是天然免疫 XSS Attack 的
《What's wrong with ASP.NET? HTML encoding》
http://msmvps.com/blogs/calinoiu/archive/2006/06/13/102957.aspx
《Which ASP.NET Controls Automatically Encodes?》
https://blogs.msdn.com/sfaust/archive/2008/09/02/which-asp-net-controls-automatically-encodes.aspx
《下面是一个很全的 "不安全控件及属性" 的 "黑名单"》
https://blogs.msdn.com/sfaust/attachment/8918996.ashx
我的方案目前还只是一个原型
就是只将 ASP.Net WebForm Sever WebControl/HtmlControl 的一些与"文本展现或输出"相关的属性统一处理的方案
目前在遍历所有控件的过程中,利用反射(性能)只处理了
System.Web.UI.WebControls 命名空间下的控件的"Text"属性
System.Web.UI.HtmlControls 命名空间下的控件的"Value"属性
发现天然能够干预处理程序代码动态 Add 的 Control
漏处理了其他命名的展现相关属性
而且还有一些过多处理了 WebForm Page 默认自动添加的控件的情况,因此还很不完善。
不能处理其他形式的 Response 输出(例如: Response.Write、前代码的<%= 后代码类成员变量%>),
自定义的 UserControl、WebCustomControl 的控件如果非"Text"或"Value"命名的展现属性也无法处理,
当然也有可能误伤与展现无关的"Text"或"Value"属性。
该方案逐步完善后,理论上,应该能"忽略"掉不必处理的控件及属性,而且还要按不安全"控件及其属性"的各种情况,分别处理,还是有一定的工作量。
另外性能尚未评估,遍历控件可能存在一定瓶颈,
其实我认为使用 HttpModule.dll、HttpHandler.dll、Global.asax 本身也存在一些性能瓶颈,因为所有的请求与输出都要流经此处。
以下完整代码
ControlsPropertyFilterHttpModule.cs
web.config
test.aspx (注意有XSS Attack 测试代码,不必惊慌只是 alert)
*/
//ControlsPropertyFilterHttpModule.cs
namespace Microshaoft
{
    using System;
    using System.Web;
    using System.Web.UI;
    using System.Reflection;
    public class ControlsPropertyFilterHttpModule : IHttpModule
    {
        private HttpApplication _contextApplication;
        public void Init(HttpApplication context)
        {
            _contextApplication = context;
            _contextApplication.PostMapRequestHandler += new EventHandler(_contextApplication_PostMapRequestHandlerProcess);
        }
        public void Dispose()
        {
            _contextApplication = null;
            _contextApplication.Dispose();
        }
        public void _contextApplication_PostMapRequestHandlerProcess(object sender, EventArgs e)
        {
            IHttpHandler handler = null;
            if (_contextApplication == null)
            {
                return;
            }
            if (_contextApplication.Context.Handler is Page)
            {
                handler = _contextApplication.Context.Handler;
            }
            if (handler != null)
            {
                Page page = handler as Page;
                //page.PreRender += new EventHandler(page_PreRender);
                page.PreRenderComplete += new EventHandler(page_PreRender);
            }
        }
        private void page_PreRender(object sender, EventArgs e)
        {
            Page page = sender as Page;
            ControlCollection cc = page.Controls;
            RecursiveProcessControls(cc);
        }
        private static void RecursiveProcessControls(ControlCollection cc)
        {
            foreach (Control c in cc)
            {
                Type t = c.GetType();
                //需要按控件种类分情况处理各种文本显示相关的属性
                //目前只处理 Text 和 Value 属性
                PropertyInfo pi = t.GetProperty("Text"); //Server WebControls
                if (pi != null)
                {
                    string s = (string) (pi.GetValue(c, null));
                    s = string.Format("Microshaoft处理了[{0}]", s);
                    //s = HttpUtility.HtmlEncode(s); //AntiXSS
                    pi.SetValue(c, s, null);
                }
                pi = t.GetProperty("Value"); //Server HtmlControls
                if (pi != null)
                {
                    string s = (string) (pi.GetValue(c, null));
                    s = string.Format("Microshaoft处理了[{0}]", s);
                    //s = HttpUtility.HtmlEncode(s); //AntiXSS
                    pi.SetValue(c, s, null);
                }
                if (c.HasControls())
                {
                    RecursiveProcessControls(c.Controls);
                }
            }
        }
    }
}


//test.aspx
<%@ Page language="c#" AutoEventWireup ="true"%>
<%@ Import Namespace="System.Data" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
    <HEAD>
        <title>WebForm1</title>
        <meta name="generator" content="editplus" />
        <meta name="author" content="" />
        <meta name="keywords" content="" />
        <meta name="description" content="" />
    <script language="C#" runat="server">
    protected void Page_Load(object sender, EventArgs ea) 
    {
        h1.Text += "<script>alert('HL Xss Attack')" + "</scr" + "ipt>";
        DataTable dt = MakeTable("F1","F2");
        string expression = "1 = 1 or f1 = '2'"; 
        DataView dv = dt.DefaultView; 
        dv.Sort = "f1 desc"; 
        dv.RowFilter = expression; 
        datagrid1.DataSource = dv;
        datagrid1.DataBind();
        gridview1.DataSource = dv;
        gridview1.DataBind();
        TextBox tb = new TextBox();
        tb.Text = "动态 TextBox";
        p1.Controls.Add(tb);
    }
    void datagrid1_ItemDataBound(object sender, DataGridItemEventArgs e) 
    {
///        foreach (TableCell cell in e.Item.Cells)
///        {
///            if (cell.Text != string.Empty)
///            {
///                cell.Text = HttpUtility.HtmlEncode(cell.Text);
///            }
///        }
    }
    void gridview1_RowDataBound(object sender, GridViewRowEventArgs e) 
    {
///        foreach (TableCell cell in e.Row.Cells)
///        {
///            if (cell.Text != string.Empty)
///            {
///                cell.Text = HttpUtility.HtmlEncode(cell.Text);
///            }
///        }
    }
    private static DataTable MakeTable
                        (
                            string c1Name
                            , string c2Name
                        )
    {
        DataTable table= new DataTable();
        DataColumn column = new DataColumn(c1Name, typeof(int));
        table.Columns.Add(column);
        column = new DataColumn(c2Name, typeof(string));
        table.Columns.Add(column);
        table.Rows.Add(1,"<script>alert('datagrid xss attack')</scr" + "ipt>");
        table.Rows.Add(2, "\u003c" + "scr" + "ipt\u003ealert\u0028\u0022gridview XSS \u0041ttack\u0022\u0029\u003c/script\u003e");
        return table;
    }
    </script>
    </HEAD>
    <body MS_POSITIONING="GridLayout">
        <form id="Form1" method="post" runat="server">
            <asp:TextBox ID="tb1" Text="静态 textBox" runat="server" />
            <asp:Label ID="l1" Text="静态 label" runat="server" />
            <asp:Panel id="p1" runat="server"/>
            <asp:Hyperlink ID="h1" NavigateUrl="http://www.baidu.com" runat="server">
                    百度 Hyperlink<script>alert('HL Xss Attack')</script>
            </asp:Hyperlink>
            <input ID="Value2" Type="Text" Value="静态 HtmlControls HtmlInputText" runat="server"/>
            <asp:ListBox ID="lb1" Width="" runat="server">
                <asp:ListItem><script>alert('LB Xss Attack')</script></asp:ListItem>
                <asp:ListItem></asp:ListItem>
            </asp:ListBox>
            <ASP:DataGrid ID="datagrid1" runat="server"
                AutoGenerateColumns="True"
                OnItemDataBound = "datagrid1_ItemDataBound"
            />
            <ASP:GridView ID="gridview1" runat="server"
                AutoGenerateColumns="True"
                OnRowDataBound = "gridview1_RowDataBound"
            />
        </form>
    </body>
</HTML>

posted @ 2009-01-08 00:08 Microshaoft 阅读(...) 评论(...) 编辑 收藏