My use-case includes a front-end WYSIWYG editor. Taking user input in HTML5/CSS format from the CSHTML Front-End View. Receiving the input in the Backend
I ended up using Custom Model Binding which bypassed this overly-eager sanitization/data loss. As a result preserved the HTML tags I want.
However this introduces XSS risk. To counter-react passing unsafe data, I used HtmlSanitizer to omit unsafe HTML/CSS tags.
Added [ModelBinder(typeof(AllowSanitizedHtmlBinder))]
annotation to parameter
[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");
}
This custom model binder is like a relay and prevents any data loss in our POST parameter. HtmlSanitizer
was used here before binding the value to prevent 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;
}
}
With my working solution above, I still have no clue why HTML markup are sanitized and removed by default. Even though everyone is claiming this is not supported and such responsibility is app-specific.
As seen here 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
Any help in demystifying the root cause would be HUGELY appreciated.
My solution was based on:
request.Unvalidated
is no longer supported.