构建一个输入文本区域以允许HTML,但防止安全/脚本攻击



平台:ASP.NET 4.0 MVC 4 C#jQuery

这是我想做的。

我正在为我的产品建立一个简单的论坛。我想给用户一个文本区域来输入他们的帖子或评论。

  1. 我想允许基本的文本格式HTML和链接,如p,a,b,I
  2. 不需要任何其他的html样式,例如div、span等
  3. 不希望有任何脚本访问权限

有什么聪明的方法可以做到这一点吗?例如,我可以允许不安全的文本并在服务器端检查它,但我怀疑我是否能够正确地清理它,可能会打开安全漏洞。

最好要避免重型插件。

谢谢!

(PS-我最糟糕的后备方法是只允许安全文本,即保持ASP.NET安全,然后对链接使用特殊标记,如[link][b][I])

更新(2020年2月):微软的AntiXSS库在其Sanitizer类上包含一个名为GetSafeHtmlFragment的静态方法,该方法似乎可以完成任务。(由@exploring.cheaily.impresses建议)


在.NET 4.5+中,或者通过将System.Web.Security.AntiXss添加到旧版本的.NET中,有一种很好的方法可以解决此问题。我们可以一起使用[AllowHtml]和自定义注释属性。该方法应该将字符串中的HTML标记列入白名单,并验证请求。

以下是此作业的自定义注释属性:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public sealed class RemoveScriptAttribute : ValidationAttribute
{
    public const string DefaultRegexPattern = @"<((?=(?!b(a|b|i|p)b))(?=(?!/b(a|b|i|p)b))).*?>";
    public string RegexPattern { get; }
    public RemoveScriptAttribute(string regexPattern = null)
    {
        RegexPattern = regexPattern ?? DefaultRegexPattern;
    }
    protected override ValidationResult IsValid(object value, ValidationContext ctx)
    {
        var valueStr = value as string;
        if (valueStr != null)
        {
            var newVal = Regex.Replace(valueStr, RegexPattern, "", RegexOptions.IgnoreCase, new TimeSpan(0, 0, 0, 0, 250));
            if (newVal != valueStr)
            {
                var prop = ctx.ObjectType.GetProperty(ctx.MemberName);
                prop.SetValue(ctx.ObjectInstance, newVal);
            }
        }
        return null;
    }
}

然后,您应该用[AllowHtml]和[RemoveScript]属性来装饰您想要HTML的模型属性,如下所示:

public class MyModel
{
    [AllowHtml, RemoveScript]
    public string StringProperty { get; set; }
}

这将只允许<a>lt;b>lt;i>,并且<p>html标签来获取它。所有其他标签都将被删除,但它足够聪明,可以保留标签的内部文本。例如,如果您发送:

"这是一个<b>富文本<b>由<u>约翰·史密斯<u>quot;

你最终会得到这个:

"这是一个<b>富文本<b>由约翰·史密斯输入"

将更多的HTML标签列入白名单也很容易。例如,如果您想接受<u>lt/u>,<br/>,并且<hr/>,更改DefaultRegexPattern(全局影响)或将修改后的regexPattern传递给RemoveScriptAttribute的实例,如下所示:

[AllowHtml]
[RemoveScript(regexPattern: @"<((?=(?!b(a|b|i|p|u|br|hr)b))(?=(?!/b(a|b|i|p|u)b))).*?>")]
public string Body { get; set; }

无论使用何种方法,都需要假设输入字段的所有内容都是恶意的,即不信任任何数据。

我不会太在意JavaScript/jQuery中的任何客户端验证。它将是复杂的,只需要在服务器端重做。

服务器端您希望采用白名单方法,即如果它不在列表中,则它是无效的。您将无法使用XML处理器,因为用户的文本可能无法生成有效的XML,相反,您可能希望使用正则表达式。

我会定义一组有效的标签(你说过p、a、b和I,但我会厌倦最后两个,因为你几乎永远不会在"wild"html中得到它们),然后我会定义这些标签是否有效以及哪些属性有效。我猜你至少想要a上的href。

您可以删除标记中不匹配的任何文本。。。我的regex技能不太好,但这似乎能找到你想要保留的所有标签,它需要反转。

<ashref=".[^"]*">|</?[abip]s?>

有很多在线编辑器可供您使用。我在谷歌上输入了"在线文本编辑器免费",并得到了一群编辑的评论。

如果您必须在标记中使用html,那么当您发现不"安全"的标记时,您将希望解析提交的文本以拒绝文本。

仅供参考,你可能对此感兴趣https://meta.stackexchange.com/questions/121981/stackoverflow-official-wmd-editor

我把joocer的回答标记为"答案",因为这有助于我形成自己的观点(尽管他说的最终不是我做的)

我决定了一个简单的规则-我会链接http://....链接并禁止任何其他html(这对我的应用程序来说很好)。通过这种方式,我让ASP.NET框架进行所有的错误检查,并禁止任何HTML标记。然后,当我在客户端上呈现文本时,我只通过用标记进行装饰来识别和修改http://链接,而HTML则对其他内容进行安全编码。

最新更新