I have an app that allows a user to edit email templates. The email templates employ HTML so the client sends HTML to the controller. I am using the Jodit HTML editor (https://xdsoft.net/jodit/) for the message body so users don't have to know HTML themselves. I can post the form and the controller accepts the request as the view model is decorated with the appropriate [AllowHtml] attribute; indeed, ModelState.IsValid is true. The error occurs when the data is returned to the client; the controller does not return the json object but instead returns an error.

The question is, how do I prevent asp.net from marking this as dangerous on return?

Note, this app deals with PII so not validating requests is not an option.

Here is the error (Adding the complete error in case someone finds that helpful):

Server Error in '/ReallyAwesomeApp' Application.

A potentially dangerous Request.Form value was detected from the client (messagetext="<font color="#000000...").

Description: ASP.NET has detected data in the request that is potentially dangerous because it might include HTML markup or script. The data might represent an attempt to compromise the security of your application, such as a cross-site scripting attack. If this type of input is appropriate in your application, you can include code in a web page to explicitly allow it. For more information, see http://go.microsoft.com/fwlink/?LinkID=212874.

Exception Details: System.Web.HttpRequestValidationException: A potentially dangerous Request.Form value was detected from the client (messagetext="<font color="#000000...").

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[HttpRequestValidationException (0x80004005): A potentially dangerous Request.Form value was detected from the client (messagetext="<font color="#000000...").] System.Web.HttpRequest.ValidateString(String value, String collectionKey, RequestValidationSource requestCollection) +322
System.Web.<>c__DisplayClass280_0.b__0(String key, String value) +18
System.Web.HttpValueCollection.EnsureKeyValidated(String key) +86
System.Web.HttpValueCollection.Get(String name) +17
System.Web.Caching.OutputCacheModule.CreateOutputCachedItemKey(String path, HttpVerb verb, HttpContext context, CachedVary cachedVary) +694 System.Web.Caching.OutputCacheModule.CreateOutputCachedItemKey(HttpContext context, CachedVary cachedVary) +56
System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs) +1226
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +200 System.Web.<>c__DisplayClass285_0.b__0() +24 System.Web.StepInvoker.Invoke(Action executionStep) +100
System.Web.<>c__DisplayClass4_0.b__0() +17
Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule.OnExecuteRequestStep(HttpContextBase context, Action step) +64
System.Web.<>c__DisplayClass284_0.b__0(Action nextStepAction) +54 System.Web.StepInvoker.Invoke(Action executionStep) +84 System.Web.<>c__DisplayClass4_0.b__0() +17 Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule.OnExecuteRequestStep(HttpContextBase context, Action step) in E:\A_work\21\s\WEB\Src\Web\Web.Shared.Net\ApplicationInsightsHttpModule.cs:164 System.Web.<>c__DisplayClass284_0.b__0(Action nextStepAction) +54 System.Web.StepInvoker.Invoke(Action executionStep) +84
System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step) +100
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +73

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.8.4075.0

Here is my view model:

public class EmailTemplateViewModel
{
    public IEnumerable<SelectListItem> EmailTemplates { get; set; }

    public List<EmailAttachmentViewModel> EmailAttachments { get; set; } = new List<EmailAttachmentViewModel>();

    public string CreateUserIdentifier { get; set; }

    public int TemplateID { get; set; }

    [Display(Name = "Template Name")]
    public string TemplateName { get; set; }

    [Display(Name = "Email Subject")]
    public string EmailSubject { get; set; }

    [AllowHtml]
    [Display(Name = "Message Text")]
    public string MessageText { get; set; }
}

Here's my return model (Found this little gem here on SO):

public class JsonReturnModel<T>
{
    public List<ClientError> ClientErrors { get; internal set; } = new List<ClientError>();

    public T Data { get; internal set; }

    public bool LoggedIn { get; internal set; }

    public string Message { get; set; }

    public List<ServerError> ServerErrors { get; internal set; } = new List<ServerError>();

    public bool Success { get; internal set; }
}

Here's my Controller method:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult EditEmailTemplate(EmailTemplateViewModel model)
    {
        RepositoryResult result = new RepositoryResult();
        JsonReturnModel<EmailTemplateViewModel> returnModel = new JsonReturnModel<EmailTemplateViewModel>();

        if (model == null)
        {
            returnModel.Success = false;
            returnModel.Message = "No data sent to server.";

            return Json(returnModel);
        }
        
        EmailTemplateModel newModel = null;
        result = DataManager.UpdateEmailTemplate(model.ToEmailTemplateModel());

        if (result.IsSuccessful)
        {
            newModel = (EmailTemplateModel)result.ResultingObject;
            returnModel.Data = newModel.ToEmailTemplateViewModel();
        }
        
        returnModel.Success = result.IsSuccessful;
        returnModel.Message = result.Message;
        
        return Json(returnModel);
    }

And finally the Ajax that gets called via JQuery:

$('#save-btn').on('click', function (event) {
    var rawHtml = $('#message-editor').html();
    $('#MessageText').val(ESCM.joditEditor.value);

    $.ajax({
        type: "post",
        url: ESCM.EmailPostUrl,
        data: $("form").serialize(), 
        async: false,
        success: function (data) {
            if (data.IsSuccessful || data.Success) {
                $('#email-template-editor').click();
                $('#TemplateID').val(data.Data.TemplateID);
                $('#TemplateID').change();
            }
            DisplayMessage(data);
        },
        error: function (errorData) {
            console.debug(errorData);
        }
    });
});
0

There are 0 answers