我有一个允许用户编辑电子邮件模板的应用程序。电子邮件模板使用 HTML,因此客户端将 HTML 发送到控制器。我正在使用 Jodit HTML 编辑器 ( https://xdsoft.net/jodit/ ) 作为消息正文,因此用户不必自己了解 HTML。我可以发布表单并且控制器接受请求,因为视图模型装饰有适当的 [AllowHtml] 属性;的确,ModelState.IsValid
是真的。数据返回给客户端时发生错误;控制器不返回 json 对象,而是返回错误。
问题是,如何防止 asp.net 在返回时将此标记为危险?
请注意,此应用程序处理 PII,因此不验证请求不是一种选择。
这是错误(添加完整的错误以防有人发现有帮助):
“/ReallyAwesomeApp”应用程序中的服务器错误。
从客户端检测到具有潜在危险的 Request.Form 值(messagetext="<font color="#000000...")。
说明: ASP.NET 在请求中检测到具有潜在危险的数据,因为它可能包含 HTML 标记或脚本。这些数据可能表示试图破坏您的应用程序的安全性,例如跨站点脚本攻击。如果这种类型的输入适合您的应用程序,您可以在网页中包含代码以明确允许它。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=212874。
异常详细信息: System.Web.HttpRequestValidationException:从客户端检测到潜在危险的 Request.Form 值(messagetext="<font color="#000000...")。
源错误:
在执行当前 Web 请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常起源和位置的信息。
堆栈跟踪:
[HttpRequestValidationException (0x80004005):从客户端检测到潜在危险的 Request.Form 值 (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(字符串路径,HttpVerb 动词,HttpContext 上下文,CachedVary cachedVary)+694 System.Web.Caching.OutputCacheModule.CreateOutputCachedItemKey(HttpContext 上下文,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版本信息:Microsoft .NET Framework 版本:4.0.30319;ASP.NET 版本:4.8.4075.0
这是我的视图模型:
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; }
}
这是我的回归模型(在 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; }
}
这是我的控制器方法:
[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);
}
最后是通过 JQuery 调用的 Ajax:
$('#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);
}
});
});