MVC Anti-XSS方案

1:Form提交模式

每当行使Form提交时,MVC框架提供了一个默认的编制。如果数额中隐含恶意字,则会自行转接出错页面。

 图片 1

 

2:Ajax+JSON提交模式。

MVC框架未供于Json数据的AntiXSS支持,所以必须自行实现。

 

Step1:定义一个Attribute[AllowHtml],如果生其一标记,则说明该属性允许Html,不需要验证。

图片 2图片 3

/// <summary>
    /// 用于标记某个属性是否允许html字符
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class AllowHtmlAttribute : Attribute
    {

    }

View Code

Step2:元数据解析的下,动态设定标记为AllowHtml的习性不刺激验证。 

图片 4图片 5

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;
                }
            }
        }
    }

View Code

Step3:实现从定义ModerBinder,拦截所有的json数据,进行anti-xss验证。 

图片 6图片 7

/// <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;
        }
    }
}

View Code

Step4:在Web站点启动的当儿,配置MVC框架 

图片 8图片 9

RouteTableRegister.RegisterRoutes(_routes);

            BundleConfig.RegisterBundles(BundleTable.Bundles);

            ModelMetadataProviders.Current = new CustomModelMetadataProvider();
            ModelBinders.Binders.DefaultBinder = new AntiXssModelBinder();

View Code

举例: 

图片 10图片 11

[Required]
     [AllowHtml]
     public string UserName{get;set;}
     [Required]
     public string Password{get;set;}
     注意:这时通过JSON传过来的数据Password就会报错,而UserName则不会。

View Code

如果Ajax传入的JSON是包好之对象,最好也使透过包装,以下也例: 

图片 12图片 13

 public ActionResult Login(LoginModel model,[ModelBinder(typeof(AntiXssRawModelBinder))])
     {
     }


    //AntiXssRawModelBinder核心代码
    /// <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;
        }
    }

View Code

倘若某些参数需要支持有HTML代码,可以用先期以拖欠参数设置为[AllowHTML],值传到Action时,再展开手动过滤,或者
使用AntiXSS库进行编码(微软资的AntiXSSLibrary类库)。 

相关文章