You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC中如何通过重写过滤器实现应用层输出编码防XSS攻击

ASP.NET MVC 全局自动输出编码实现方案

在ASP.NET MVC中通过过滤器实现全局输出编码,优先选用IResultFilter实现,可同时覆盖视图返回、JSON返回两类常见场景,自动对所有字符串内容做HTML编码,避免XSS风险。

1. 自定义编码过滤器代码

using System.Web.Mvc;
using System.Text.Encodings.Web;
using System.Collections.Generic;

public class AutoOutputEncodingFilter : IResultFilter
{
    private readonly HtmlEncoder _htmlEncoder;

    public AutoOutputEncodingFilter()
    {
        _htmlEncoder = HtmlEncoder.Default;
    }

    public void OnResultExecuting(ResultExecutingContext filterContext)
    {
        // 跳过标记了忽略编码的控制器/方法
        var skipEncoding = filterContext.ActionDescriptor.IsDefined(typeof(SkipAutoEncodingAttribute), true)
            || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(SkipAutoEncodingAttribute), true);
        if (skipEncoding) return;

        // 处理视图返回场景,编码ViewData、ViewBag中的字符串
        if (filterContext.Result is ViewResult viewResult)
        {
            // 编码ViewData
            foreach (var key in viewResult.ViewData.Keys)
            {
                if (viewResult.ViewData[key] is string rawStr && !string.IsNullOrWhiteSpace(rawStr))
                {
                    viewResult.ViewData[key] = _htmlEncoder.Encode(rawStr);
                }
            }

            // 编码ViewBag
            var viewBagItems = viewResult.ViewBag as IDictionary<string, object>;
            if (viewBagItems != null)
            {
                foreach (var key in viewBagItems.Keys)
                {
                    if (viewBagItems[key] is string rawStr && !string.IsNullOrWhiteSpace(rawStr))
                    {
                        viewBagItems[key] = _htmlEncoder.Encode(rawStr);
                    }
                }
            }
        }
        // 处理JSON返回场景,递归编码所有字符串字段
        else if (filterContext.Result is JsonResult jsonResult && jsonResult.Data != null)
        {
            EncodeAllStringFields(jsonResult.Data, _htmlEncoder);
        }
    }

    public void OnResultExecuted(ResultExecutedContext filterContext)
    {
        // 无需处理结果执行后逻辑
    }

    /// <summary>
    /// 递归编码对象中所有字符串类型的属性/元素
    /// </summary>
    private void EncodeAllStringFields(object data, HtmlEncoder encoder)
    {
        if (data == null) return;
        var dataType = data.GetType();

        // 跳过基础值类型
        if (dataType.IsPrimitive || dataType == typeof(DateTime) || dataType == typeof(decimal)) return;

        // 直接编码字符串
        if (data is string rawStr)
        {
            encoder.Encode(rawStr);
            return;
        }

        // 处理集合类型
        if (data is System.Collections.IEnumerable collection)
        {
            foreach (var item in collection)
            {
                EncodeAllStringFields(item, encoder);
            }
            return;
        }

        // 处理自定义类的属性
        var properties = dataType.GetProperties();
        foreach (var prop in properties)
        {
            if (prop.PropertyType == typeof(string) && prop.CanRead && prop.CanWrite)
            {
                var propValue = prop.GetValue(data) as string;
                if (!string.IsNullOrWhiteSpace(propValue))
                {
                    prop.SetValue(data, encoder.Encode(propValue));
                }
            }
            else if (prop.PropertyType.IsClass && prop.CanRead)
            {
                EncodeAllStringFields(prop.GetValue(data), encoder);
            }
        }
    }
}

2. 可选:定义跳过编码的标记Attribute

针对部分不需要编码的场景,可自定义标记直接跳过过滤器逻辑:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class SkipAutoEncodingAttribute : Attribute
{
}

3. 全局注册过滤器

修改App_Start/FilterConfig.cs,把自定义过滤器加入全局过滤集合,所有Action默认生效:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        // 注册全局自动编码过滤器
        filters.Add(new AutoOutputEncodingFilter());
    }
}

注意事项

  • 可根据业务需求调整编码逻辑,比如对富文本字段单独加白名单规则,避免把合法的HTML标签也编码
  • 注意避免二次编码问题,可在编码前加判断逻辑,识别已经编码过的内容直接跳过
  • Razor视图默认的@输出语法已经自带编码能力,该过滤器主要对ViewData/ViewBag动态内容、直接返回前端渲染的JSON内容做额外防护

内容的提问来源于stack exchange,提问作者shafi ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 04:57:03