已经讨论了这个问题两天,没有任何真正的运气。我在客户端使用带有 jquery ajax asp.net webapi2。
我有一个用于输入备忘录文本的编辑框,允许的字符^[©a-zA-Z0-9u0900-u097f,.s-'"!?()[]]+$
,两个标签<LineBreak/>
和<Link attr="value"/>
(可能是链接标签中的更多属性。问题是不允许使用其他标签 - 这意味着即使是简单的<br/>
也应该被阻止。事实证明,这种负面支票有点复杂。
请求帮助在客户端为 javascript 制定正则表达式,在服务器端为 c# 制定基于 c# 的数据注释检查。
你试图做的是清理用户输入,但是,使用 JavaScript 和正则表达式是错误的。
不要关心在前端验证用户输入,至少现在还没有,重点应该首先是验证服务器端,这项工作的最佳工具是 HtmlSanitizer。用他们的话说:
HtmlSanitizer是一个.NET库,用于从可能导致XSS攻击的构造中清除HTML片段和文档。
HtmlSanitizer可以在几个级别进行自定义:
- 通过属性 AllowedTags 配置允许的 HTML 标记。
- 通过属性 AllowedAttributes 配置允许的 HTML 属性。
- 通过属性 AllowedCssProperties 配置允许的 CSS 属性名称。
- 通过属性 AllowedAtRules 配置允许的 CSS at-rules。
- 通过属性 AllowedSchemes 配置允许的 URI 方案。
- 配置包含 URI 的 HTML 属性(例如"src"、"href"等(
- 提供将用于解析相对 URI 的基本 URI。
- 在删除标记、属性或样式之前,会引发可取消的事件。
我已经模拟了一个关于使用该库 dotnetfiddle.net 的演示供您使用
void Main()
{
var allowedTags = new[]{"LineBreak", "Link"};
var allowedAttributes = new[]{"attr"};
var sanitizer = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes);
//sanitizer.
var html = @"<script>alert('xss')</script><div onload=""alert('xss')""" + @"style=""background-color: test"">Test<img src=""test.gif""" + @"style=""background-image: url(javascript:alert('xss')); margin: 10px""></div>
<LineBreak></LineBreak>
<Link attr=""v123""/>";
var sanitized = sanitizer.Sanitize(html);
Console.WriteLine(sanitized);
}
编辑
但是想知道为什么"正则表达式是错误的处理方式"。
正则表达式不是为这种类型的任务而设计的,你需要能够解析一个 html 文档,这意味着在树状结构中解析其标签、属性和这些属性中的值,以便能够正确清理它,因为有太多的边缘情况太难用正则表达式来覆盖。正则表达式更适合用于从已经处于可预测结构中的源中抓取数据,用户输入不是其中之一。
即使您的用例足够简单,您仍然允许用户输入 HTML,这些 HTML 将以原始格式重新显示给其他用户,因此您错过的任何内容都会让您头疼。
这是来自 OWASP 的 XSS 过滤器规避备忘单,如果正则表达式可以涵盖此处列出的所有内容,我会说很好,但在正则表达式中实现这一目标是一项艰巨的任务,以至于它没有意义。
另一方面,HtmlSanitizer确实涵盖了备忘单上列出的问题,它也得到了积极的维护,并且是专门为此类应用程序构建的,它也不是笨重的,它可以处理大型清理任务,处理时间在50-100ms范围内。
通过组合允许尖括号(从而自定义标签(的正则表达式数据注释来实现这一点
[RegularExpression(@"([©a-zA-Z0-9u0900-u097f,.s-'""!?()[]<>/]*)")]
以及一个 ValidationAttribute 类,用于检查不需要的标记(换行符和链接除外(
public class CustomTagValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
Regex re = new Regex(@"(<(?!(LineBreaks*|Links+[sw'""=]*)/?>))", RegexOptions.Multiline);
return re.Match(value.ToString()).Length == 0 ? ValidationResult.Success : new ValidationResult(Resources.ErrorStrings.InvalidValuesInRequest);
}
}
这两个属性都应用于类属性,如下所示 -
[CustomTagValidator]
[RegularExpression(@"([©a-zA-Z0-9u0900-u097f,.s-'""!?()[]<>/]*)")]
public string PropertyToValidate { get; set; }
还添加了一个 ActionFilterAttribute,以确保在调用控制器操作之前执行验证检查 -
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
并将其应用于相关控制器操作,如下所示 -
[ValidateModel]
public HttpResponseMessage Post([FromBody] MyModel mm)
希望这有助于遇到类似问题的人。
几乎忘记了,使用相同的基于正则表达式的 JavaScript 验证在客户端应用了相同的解决方案。