JavaScript And Ajax(呈现脚本块、脚本注入攻击)

呈现脚本块

       目前为止的 js 都是直接插入页面 .aspx 部分的静态 <script> 块。然而,使用公开 ClientScriptManager 对象的 Page.ClientScript 属性呈现脚本通常更加灵活。

       ClientScriptManager 提供了几个管理脚本块的有用方法,其中两个最有用的方法如下:

  • RegisterClientScriptBlock() :把脚本块写到 Web 表单的开始,在 <form runat=”server”> 标签后。适合响应 js 事件而被调用的函数使用。这样的 <script> 块可以放在任意地方,放在 Web 表单开头只是出于习惯并使它们容易被找到
  • RegisterStartupScript() :把脚本块写到 Web 表单的结尾,在结束标签 </form> 之前。用于添加那些页面加载时立刻执行的 js 代码。这些代码可能操纵页面上的其他控件,所有放在 Web 表单的结尾比较安全。

       使用这 2 个方法,需要指定脚本块的键名。唯一的键名,好处是能保证 ASP.NET 不会把相同的脚本函数添加多次。例如 ASP.NET 验证控件,每个控件都要使用一些验证函数,但是让每个控件都添加重复的 <script> 块并没有意义。但当调用 RegisterClientScriptBlock() 时,每个控件都使用相同的键名,ASP.NET 知道它们是重复的定义,所以只呈现一份单一副本。

 

       下面的代码注册一个 confirmSubmit() 的 js 函数,通过 onsubmit 特性附加到表单上:

protected void Page_Load(object sender, EventArgs e)
{
    string script = @"<script type='text/javascript'>
        function confirmSubmit(){
            var msg = 'Are you sure you want to submit this data?';
            return confirm(msg);
        }
    </script>";
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "Confirm", script);
    form1.Attributes.Add("onsubmit", "return confirmSubmit();");
}

       本示例并没有从 RegisterClientScriptBlock() 方法中受益。但是,使用 js 创建一个自定义控件时,ClientScriptMananger 方法就非常重要了!

 

 

脚本注入攻击

       开发人员并非不能意识到页面中的安全问题,只是因为很多常见的危险(包括 脚本注入 和 SQL注入)会在不经意间发生。

       一个常施加于网页的攻击是脚本注入攻击。用户提交恶意标签或脚本代码(一般通过 TextBox 控件之类的简单控件),然后呈现到 HTML 页面,这时就会发生脚本注入攻击。如果用户提供的数据被保存到数据库并插入到其他用户使用的页面,攻击可以影响网站所有用户的操作

       脚本注入攻击的基本技术是客户端提交嵌入了脚本标签的内容。这些脚本标签可以包括 <script>、<object>、<applet> 和<embed>。虽然应用程序可以具体检查这些标签并用 HTML 编码来使用无害的 HTML 实体代替这些标签,但是通常不会执行基本验证。

 

请求验证

       脚本注入攻击是所有 Web 开发人员都关注的,无论他们使用的是 ASP.NET、ASP 还是其他 Web 开发技术。ASP.NET 包含一个设计用于自动防止脚本注入攻击的功能,叫做请求验证。它检查回传的表单输入,如果发现潜在的恶意标签(如 <script>),就报告一个错误。实际上,请求验证禁止所有非数值的标签,包括 HTML 标签( 如<b> 或 <img>等)和不正确的标签(如<abcd>)。

       创建一个页面可测试请求验证功能:

image

       现在尝试提交的话,如果是本地运行代码,可以看到下面的错误详细信息(远程只会看见一般的错误页面):

image

 

禁用请求验证

       当然,有时,请求验证规则有点太严格了。例如,有的程序确实需要用户输入 HTML 标签或者 XML 数据块。例如用户要提交一个拍卖列表或者销售广告的格式化 HTML 块的 Web 应用。

       这些情况下,你可以 2 步禁用请求验证

       首先,设置 Page 指令,不过这还不能立刻带来变化。因为 ASP.NET 4 改变了请求验证的工作方式。现在它在页面处理生命周期之前被应用

<%@ Page ValidateRequest="false" ... %>

       其次,配置 web.config 文件,告诉 ASP.NET 4 恢复以前版本的验证行为:

<system.web>
    <httpRuntime requestValidationMode="2.0"/>
    ...
</system.web>

       现在,想象一下如果使用这段代码在标签里显示用户提供的值会发生什么:

protected void Button1_Click(object sender, EventArgs e)
{
    lblInfo.Text = "You entered: " + txtInput.Text; 
}

image 

image

       也可以修改 web.config 文件来禁用整个 Web 应用程序的请求验证。设置或增加 <pages> 元素的 validateRequest 属性:

<system.web>
    <pages validateRequest="false">
    <httpRuntime requestValidationMode="2.0"/>
    ...
</system.web>

 

       脚本注入攻击中的脚本总是在客户端执行。但是,这并不意味着它仅仅限于单一用户!大多情况下,用户提供的数据被保存到一个数据库之类的地方,并且能够被其他用户浏览。例如,一个用户把脚本块作为业务名称加入业务注册表,其他请求完整业务注册表的用户就会受到影响。

       在请求验证被关闭的情况下,为了阻止脚本注入攻击的发生,你需要在显示内容前使用 Server 对象显式地编码要显示的内容:

protected void Button1_Click(object sender, EventArgs e)
{
    lblInfo.Text = "You entered: " + Server.HtmlEncode(txtInput.Text);
}

image

 

扩展请求验证

       对于绝大部分 Web 应用程序,ASP.NET 请求验证能够很好的满足其验证需要。但是在某些情况下,你可能需要选择让一些通常会被拒绝的值通过验证或者要增加其他要被禁止的值,此时,需要通过自定义代码来扩展请求验证系统。(如果面临完全禁用请求验证和通过代码增加例外的两难选择,通常增加例外是更好的选择!否则,你的程序对各种脚本攻击或其他损害毫无招架之力)。

       为了扩展请求验证系统,需要派生 System.Web.Util.RequestValidator 类并重写 IsValidRequestString() 方法:

public class CustomRequestValidator : System.Web.Util.RequestValidator
{
    protected override bool IsValidRequestString(HttpContext context, 
        string value, RequestValidationSource requestValidationSource, 
        string collectionKey, out int validationFailureIndex)
    {
        ...
    }
}

       IsValidRequestString() 方法接受 5 个参数:

  1. context 。请求的 HttpContext,通过它可以访问内置的 ASP.NET 对象,如 Request、Response、Application、Server、Session 和 Cache。
  2. value 。它是要进行验证的字符串文本
  3. requestValidationSource 。它使用枚举 RequestValidationSource 来标识要验证的信息类型。可能的值包括 Cookies、File、Form、Headers、Path 和 QueryString。
  4. collectionKey 。如果数据源来自某个集合,collectionKey 返回用于索引值的键值。例如,对于表单数据,collectionKey 和回发值的输入控件相对应。
  5. validationFailureIndex 。如果验证执行成功,validationFailureIndex 应该设置为 0 并且 IsValidRequestString() 返回 true。如果验证失败,IsValidRequestString() 方法返回 false,而 validationFailureIndex 指向字符串中非法数据开始的位置。

       通过这些信息,可以很方便的创建改变 ASP.NET 内置行为的应急的验证方法。

       下面的示例里,验证器检查是否在进行表单验证。如果不是,验证器触发默认的实现。如果是,则搜索回发输入中的 <script> 标记。如果包含就拒绝该值,但同时接收其他合法的值,这使它的验证行为要比 ASP.NET 默认验证更为宽松

public class CustomRequestValidator : System.Web.Util.RequestValidator
{
    protected override bool IsValidRequestString(HttpContext context,
        string value, RequestValidationSource requestValidationSource,
        string collectionKey, out int validationFailureIndex)
    {
        if (requestValidationSource == RequestValidationSource.Form)
        {
            int errorIndex = value.ToLower().IndexOf("<script>");
            if (errorIndex != -1)
            {
                validationFailureIndex = errorIndex;
                return false;
            }
            else
            {
                validationFailureIndex = 0;
                return true;
            }
        }
        else
        {
            return base.IsValidRequestString(context, value,
                requestValidationSource, collectionKey, out validationFailureIndex);
        }
    }
}

       需要在 web.config 文件中注册它才能使用:

<system.web>
  <httpRuntime requestValidationType="CustomRequestValidator"/>
  ...
</system.web>

 

       这个示例只是简单演示了自定义验证是如何工作的。但是,对于如何学会通过自定义代码加入或多或少的验证已经足够了。

       如果要增强标准 ASP.NET 验证,最好把你的实现添加到默认的实现链里。验证值通过你的检查后,可以继续调用基类的 IsValidRequestString() 来执行额外的内置检查

posted on 2012-12-17 18:03  SkySoot  阅读(5480)  评论(0编辑  收藏  举报

导航