您的位置:首页 > 其它

MVC Anti-XSS方案

2015-06-23 17:29 671 查看
http://blog.csdn.net/cassaba/article/details/21094011

XSS攻击是用户提交到服务器的数据包含恶意JavaScript脚本,如果这种数据在存储或显示的时候不加处理,那么其它用户访问页面的时候,这些脚本可能被执行,轻则导致页面无法正常使用,重则导致重要信息泄露。

开发Web应用程序,需要从全局考虑这个问题,采取一致的处理方式,在整个开发过程中严格执行,避免产生XSS漏洞。下面分别就Form和Json两种数据提交模式,所采取的方案做一下介绍。

1. Form提交模式

在使用Form提交的时候,MVC框架提供了一个默认的机制。如果数据中含有恶意字符,则会自动转向出错界面。如下图:





2. Ajax + JSON提交模式

由于我所开发的项目,前端和后端交互主要通过Ajax + Json来进行。而MVC框架并未提供对于Json数据的anti-XSS支持, 所以必须自行实现。

基础思路是在MVC进行Model Binder的时候,检查request中的MIME类型,如果是application/json, 则接管系统默认的Model绑定和验证。

Step 1: 定义一个Attribute [AllowHtml], 用于标记Model中的属性. 如果有这个标记,则说明该属性允许Html, 不需要验证

[csharp] view
plaincopy





/// <summary>

/// 用于标记某个属性是否允许html字符

/// </summary>

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]

public sealed class AllowHtmlAttribute : Attribute

{

}

Step 2: 元数据解析的时候,动态设定标记为AllowHtml的属性不激发验证

[csharp] view
plaincopy





public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider

{

public CustomModelMetadataProvider()

{

}

protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType,

Func<object> modelAccessor, Type modelType, string propertyName)

{

var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

if (containerType == null || propertyName == null)

return metadata;

foreach (Attribute attr in attributes)

{

if (attr is AllowHtmlAttribute)

{

metadata.RequestValidationEnabled = false;

break;

}

}

}

}

Step 3: 实现自定义ModerBinder,
拦截所有json数据,进行anti-xss验证

[csharp] view
plaincopy





/// <summary>

/// 检测Json数据中含有恶意字符,抛出HttpRequestValidationException

/// </summary>

public class AntiXssModelBinder : DefaultModelBinder

{

protected override bool OnPropertyValidating(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)

{

if (controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))

{

int index;

if (controllerContext.Controller.ValidateRequest

&& bindingContext.PropertyMetadata[propertyDescriptor.Name].RequestValidationEnabled)

{

if (value is string)

{

if (AntiXssStringHelper.IsDangerousString(value.ToString(), out index))

{

throw new HttpRequestValidationException("Dangerous Input Detected");

}

}

else if (value is IEnumerable)

{

// 字符串数组或者集合,或者Dictionary<string, string>

// Dictionary的Key, Value会ToString后一起验证

foreach (object obj in value as IEnumerable)

{

if (obj != null)

{

if (AntiXssStringHelper.IsDangerousString(obj.ToString(), out index))

{

throw new HttpRequestValidationException("Dangerous Input Detected");

}

}

}

}

}

}

return base.OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, value);

}

}

/// <summary>

/// 检测绑定的单值字符串是否包含恶意字符

/// </summary>

public class AntiXssRawModelBinder : StringTrimModelBinder

{

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

{

var value = base.BindModel(controllerContext, bindingContext);

if (value is string)

{

var result = (value as string).Trim();

int index;

if (AntiXssStringHelper.IsDangerousString(value.ToString(), out index))

{

throw new HttpRequestValidationException("Dangerous Input Detected");

}

}

return value;

}

}

}

Step 4: 在Web站点启动的时候,配置MVC框架

[csharp] view
plaincopy





RouteTableRegister.RegisterRoutes(_routes);

BundleConfig.RegisterBundles(BundleTable.Bundles);

ModelMetadataProviders.Current = new CustomModelMetadataProvider();

ModelBinders.Binders.DefaultBinder = new AntiXssModelBinder();

以下面的Model为例子。

public class LoginModel

{

[Required]

[Display(Name="用户名")]

[AllowHtml]

publicstringUserName { get;set;
}

[Required]

[DataType(DataType.Password)]

[Display(Name="密码")]

publicstringPassword { get;set;
}

}

Password属性未被标记为[AllowHtml], 如果Password传递进来的数据是:<script>
alert(4)</script>qwe@1232432<b>粗体</b>

则系统会抛出异常,反之,如果UserName 传递这样的数据则不会报告异常。

如果Action参数通过简单类型传递,并且要验证XSS,则需要使用Model绑定标记, 比如下面的returnUrl.

当然实际开发的时候,最好是参数封装为对象。

publicActionResultLogin(LoginModelmodel, [ModelBinder(typeof(AntiXssRawModelBinder))]stringreturnUrl)

{

….

}

AntiXssRawModelBinder 代码:

[csharp] view
plaincopy





/// <summary>

/// 检测绑定的单值字符串是否包含恶意字符

/// </summary>

public class AntiXssRawModelBinder : StringTrimModelBinder

{

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

{

var value = base.BindModel(controllerContext, bindingContext);

if (value is string)

{

var result = (value as string).Trim();

int index;

if (AntiXssStringHelper.IsDangerousString(value.ToString(), out index))

{

throw new HttpRequestValidationException("Dangerous Input Detected");

}

}

return value;

}

}

如果某些参数需要支持部分HTML代码, 可以采取的方法是先将该参数设置为 [AllowHtml], 进到Action后,再自行过滤或者使用AntiXSS库编码。

其它:

微软提供了一个对数据进行编码解码的库,名字是AntiXSS,可以通过Nuget引入。主要工具函数如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: