将 HTML 字符串传递给我的控制器操作方法

Passing HTML string to my controller action method

我正在使用富文本编辑器键入格式化文本,如下所示:

我可以获得 HTML 格式的文本,如下所示:

<p>This is my rich HTML Text</p>

现在我想将此 HTML 格式的文本传递给我的控制器,我的控制器会将文本放入电子邮件中并将其发送给接收者。

问题是 HTML 字符串被认为是不安全的,因此为了将它传递给我的控制器,我需要将 [ValidateInput(false)] 属性添加到我的 Action 方法中,如下所示:

    [ValidateInput(false)] // <-- not able to hit the action method without this
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<JsonResult> Contact(string message)
    {
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            return Json(new { Authorize = "false" });
        }

        // email message to receiver
    }

这是联系控制器的Ajax方法:

$('#contactBtn').click(function () {
    var form = $('#__AjaxAntiForgeryForm');
    var token = $('input[name="__RequestVerificationToken"]', form).val();
    var message = quill.root.innerHTML; // <-- HTML formatted message

    $.ajax({
        url: "/Communication/Contact",
        data: { __RequestVerificationToken: token, message: message },
        dataType: 'json',
        type: "POST"
    });
});

所以上面的代码有效,但我不确定这样做是否正确?上面的代码有什么安全问题吗?我需要对 HTML 进行任何编码吗?

实际上 ValidateInput 属性与 XSS (Cross Site Security) 问题有关。

XSS(跨站点安全) 是一种安全攻击,攻击者在进行数据输入时注入恶意代码。此代码可以是 javascript、vbscript 或任何其他脚本代码。一旦代码被注入最终用户的浏览器。此代码可以 运行 并获得对 cookie、会话、本地文件等的访问权限。

现在好消息是 XSS 在 ASP.NET MVC 中默认被阻止。因此,如果有人尝试使用 post JavaScript 或 HTML 代码输入,他会出现以下错误。

A potentially dangerous Request.Form value was detected from the client.....

但在现实生活中有些情况下 HTML 必须被允许,例如 HTML 编辑器。因此,对于这些场景,我们用 ValidateInput 属性装饰我们的操作方法,如下所示:

[ValidateInput(false)]
public async Task<JsonResult> Contact(string message)
{

}

但是这样做有问题。我们允许 HTML 和脚本完成可能很危险的操作。假设我们 posting 的表单有五个输入文本字段,现在所有五个文本字段都可以包含 HTML 和脚本。

改为 Microsoft article 建议:

For ASP.NET MVC 3 or greater applications, when you need to post HTML back to your model, don’t use ValidateInput(false) to turn off Request Validation. Simply add [AllowHtml] to your model property, like so:

public class BlogEntry
{
  public int UserId {get;set;}
  [AllowHtml]
  public string BlogText {get;set;}
}

所以底线是 ValidateInput 允许脚本和 HTML 在整个操作级别上 post 编辑,而 AllowHTML 在更精细的级别上。

详情请阅读ASP.NET Security - Securing Your ASP.NET Applications

在 action 方法上使用 [ValidateInput(false)] 不是一个好方法,因为可能还有其他输入参数未经过验证...如果我们传入型号...

对于这种情况,我们可以按照 this tutorial:

中的说明进行操作

我的解决方案基于上面的教程,除了我在模型绑定器中添加了清理逻辑,这意味着我们允许 HTML 输入,但使用 HTMLSanitizer 来清理输入。

定义了自定义模型活页夹:

public class AllowHtmlBinder: IModelBinder
{
    // use HtmlSanitizer to remove unsafe HTML/JS from input
    private HtmlSanitizer _htmlSanitizer = new HtmlSanitizer();

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;
        var name = bindingContext.ModelName;
        var unvalidatedInputMessage = request.Unvalidated[name]; // get the unvalidated input
        var sanitizedMessage = _htmlSanitizer.Sanitize(unvalidatedInputMessage); // removed script or any XSS thread from user input
        return sanitizedMessage;
    }
}

并将其用于特定参数:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<JsonResult> Contact([ModelBinder(typeof(AllowHtmlBinder))] string message)
{
    if (!HttpContext.User.Identity.IsAuthenticated)
    {
        return Json(new { Authorize = "false" });
    }

    // email message to receiver
}