平台:ASP.NET 4.0 MVC 4 C#jQuery
这是我想做的。
我正在为我的产品建立一个简单的论坛。我想给用户一个文本区域来输入他们的帖子或评论。
- 我想允许基本的文本格式HTML和链接,如p,a,b,I
- 不需要任何其他的html样式,例如div、span等
- 不希望有任何脚本访问权限
有什么聪明的方法可以做到这一点吗?例如,我可以允许不安全的文本并在服务器端检查它,但我怀疑我是否能够正确地清理它,可能会打开安全漏洞。
最好要避免重型插件。
谢谢!
(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则对其他内容进行安全编码。