在 POST 请求中将原始 HTML 从视图传递到控制器时发生数据丢失 -- XSS 安全和信息丢失

Data loss when passing raw HTML from View to Controller in POST request -- XSS-safety & information loss

前言

我的用例包括前端所见即所得编辑器。从 CSHTML 前端视图中以 HTML5/CSS 格式获取用户输入。通过 POST 请求接收后端控制器操作中的输入。最后用它做一些花哨的数据库。

听起来很简单。使用这个 editor 的野兽,它非常简单且可自定义。

查看

WYSIWYG 编辑器 textarea 嵌套在 form 中,使用 POST

发送编辑器的原始 HTML 数据
    <form class="form" asp-controller="CreationController" asp-action="CreateSnowflakeBlogpost" method="post">
        <button type="submit" class="btn btn-link">Submit Snowflake Blogpost</button>
        <textarea name="snowflakeHtmlContent" id="joditEditor"> </textarea>
    </form>

控制器

控制器的动作接收POST参数。

    [HttpPost]
    public async Task<IActionResult> CreateSnowflakeBlogpost(string snowflakeHtmlContent)
    {
        // store HTML content in DB and do fancy operations

        // redirect to something else
        return RedirectToAction("PreviewSnowflakeBlogpost");
    }

问题

HTML5/CSS 标签在传递 POST 数据的过程中会丢失。经过检查,它们已从 View 成功发送。 Action 的参数虽然有不正确的数据。

这里似乎正在进行清理,去除了我们想要特意保留的 HTML 标签的 POST 参数。

看起来有可能的解决方案。


问题

如何将原始 HTML/CSS 数据从视图传递到操作?满足以下条件:

  1. 标记没有数据丢失。

  2. 防止造成 XSS 风险的不安全数据。根据 guidelines.

解决方案

我最终使用 Custom Model Binding 绕过了这个过于急切的 sanitization/data 损失。结果保留了我想要的 HTML 标签。

但是这会引入 XSS 风险。为了应对传递的不安全数据,我使用 HtmlSanitizer 来省略不安全的 HTML/CSS 标签。

动作

为参数添加了 [ModelBinder(typeof(AllowSanitizedHtmlBinder))] 注释

    [HttpPost]
    public async Task<IActionResult> CreateSnowflakeBlogpost([ModelBinder(typeof(AllowSanitizedHtmlBinder))] string snowflakeHtmlContent)
    {
        // store HTML content in DB and do fancy operations

        // redirect to something else
        return RedirectToAction("PreviewSnowflakeBlogpost");
    }

自定义模型活页夹

这个自定义模型绑定器就像一个继电器,可以防止我们的 POST 参数中的任何数据丢失。 HtmlSanitizer这里用在绑定值之前,防止XSS。

    // Custom Model Binding
    using Microsoft.AspNetCore.Mvc.ModelBinding;

    // HTML Sanitizer
    using Ganss.XSS;

    public class AllowSanitizedHtmlBinder: IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            var modelName = bindingContext.ModelName;

            // Try to fetch the value of the argument by name
            var valueProviderResult =
                bindingContext.ValueProvider.GetValue(modelName);

            if (valueProviderResult == ValueProviderResult.None)
            {
                return Task.CompletedTask;
            }

            bindingContext.ModelState.SetModelValue(modelName,
                valueProviderResult);

            var value = valueProviderResult.FirstValue;

            // Check if the argument value is null or empty
            if (string.IsNullOrEmpty(value))
            {
                return Task.CompletedTask;
            }

            // Sanitize HTML from harmful XSS markup
            var sanitizer = new HtmlSanitizer();
            var sanitizedValue = sanitizer.Sanitize(value);

            bindingContext.Result = ModelBindingResult.Success(sanitizedValue);

            return Task.CompletedTask;
        }
    }

[帮助] 缺失部分——了解根本原因

使用我上面的工作解决方案,我仍然不知道为什么 HTML 标记在默认情况下被清理和删除。尽管每个人都声称这不受支持并且这种责任是特定于应用程序的。

如所见 and here:

You don't need [AllowHtml] anymore, because nobody denies HTML in ASP.NET Core 2.0

Don't need [AllowHtml] or RequestValidationEnabled because we don't have request validation in this system

如能帮助揭开根本原因,我们将不胜感激。

来源

我的解决方案基于:

  1. This回答。尽管不再支持 request.Unvalidated
  2. Custom Model Binding.
  3. HtmlSanitizer.
  4. This 回答对我指明了正确的方向很有帮助。