每当用户在我的web应用程序中的页面中发布包含<或>的内容时,我都会引发此异常。

我不想因为有人在文本框中输入了字符而引发异常或使整个web应用程序崩溃,但我正在寻找一种优雅的方式来处理这一问题。

捕获异常并显示

出现错误,请返回并重新键入整个表单,但这次请不要使用<

我觉得不够专业。

禁用后验证(validateRequest=“false”)肯定可以避免此错误,但这会使页面容易受到许多攻击。

理想情况下:当发生包含HTML限制字符的回发时,表单集合中的回发值将自动进行HTML编码。因此,我的文本框的.Text属性将是&lt;html&gt;

有没有办法让我从处理者那里做到这一点?


当前回答

在web.config文件中的标记中,插入属性为requestValidationMode=“2.0”的httpRuntime元素。同时在pages元素中添加validateRequest=“false”属性。

例子:

<configuration>
  <system.web>
   <httpRuntime requestValidationMode="2.0" />
  </system.web>
  <pages validateRequest="false">
  </pages>
</configuration>

其他回答

正如我对Sel回答的评论所指出的,这是我们对自定义请求验证器的扩展。

public class SkippableRequestValidator : RequestValidator
{
    protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
    {
        if (collectionKey != null && collectionKey.EndsWith("_NoValidation"))
        {
            validationFailureIndex = 0;
            return true;
        }

        return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
    }
}

我想你可以在一个模块中完成;但这留下了一些问题;如果您想将输入保存到数据库中怎么办?突然间,由于您正在将编码数据保存到数据库中,您最终会信任来自数据库的输入,这可能是一个坏主意。理想情况下,您将原始未编码数据存储在数据库中,并每次进行编码。

在每页级别禁用保护,然后每次编码是更好的选择。

与其使用Server.HtmlEncode,不如看看Microsoft ACE团队提供的更新、更完整的反XSS库。

您可以在自定义模型活页夹中自动对字段进行HTML编码。我的解决方案有些不同,我将错误放在ModelState中,并在字段附近显示错误消息。很容易修改此代码以自动编码

 public class AppModelBinder : DefaultModelBinder
    {
        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            try
            {
                return base.CreateModel(controllerContext, bindingContext, modelType);
            }
            catch (HttpRequestValidationException e)
            {
                HandleHttpRequestValidationException(bindingContext, e);
                return null; // Encode here
            }
        }
        protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext,
            PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
        {
            try
            {
                return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
            }
            catch (HttpRequestValidationException e)
            {
                HandleHttpRequestValidationException(bindingContext, e);
                return null; // Encode here
            }
        }

        protected void HandleHttpRequestValidationException(ModelBindingContext bindingContext, HttpRequestValidationException ex)
        {
            var valueProviderCollection = bindingContext.ValueProvider as ValueProviderCollection;
            if (valueProviderCollection != null)
            {
                ValueProviderResult valueProviderResult = valueProviderCollection.GetValue(bindingContext.ModelName, skipValidation: true);
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
            }

            string errorMessage = string.Format(CultureInfo.CurrentCulture, "{0} contains invalid symbols: <, &",
                     bindingContext.ModelMetadata.DisplayName);

            bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorMessage);
        }
    }

在应用程序启动中:

ModelBinders.Binders.DefaultBinder = new AppModelBinder();

请注意,它仅适用于表单字段。危险值未传递到控制器模型,但存储在ModelState中,可以在表单上重新显示错误消息。

URL中的危险字符可以这样处理:

private void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    HttpContext httpContext = HttpContext.Current;

    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        var httpCode = httpException.GetHttpCode();
        switch (httpCode)
        {
            case (int)HttpStatusCode.BadRequest /* 400 */:
                if (httpException.Message.Contains("Request.Path"))
                {
                    httpContext.Response.Clear();
                    RequestContext requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
                    requestContext.RouteData.Values["action"] ="InvalidUrl";
                    requestContext.RouteData.Values["controller"] ="Error";
                    IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
                    IController controller = factory.CreateController(requestContext, "Error");
                    controller.Execute(requestContext);
                    httpContext.Server.ClearError();
                    Response.StatusCode = (int)HttpStatusCode.BadRequest /* 400 */;
                }
                break;
        }
    }
}

错误控制器:

public class ErrorController : Controller
 {
   public ActionResult InvalidUrl()
   {
      return View();
   }
}   

解决方案

我不想关闭后验证(validateRequest=“false”)。另一方面,应用程序崩溃是不可接受的,因为一个无辜的用户碰巧键入了<x或其他内容。

因此,我编写了一个客户端javascript函数(xssCheckValidates),用于进行初步检查。当试图发布表单数据时,调用此函数,如下所示:

<form id="form1" runat="server" onsubmit="return xssCheckValidates();">

该功能非常简单,可以改进,但它正在发挥作用。

请注意,这样做的目的不是为了保护系统免受黑客攻击,而是为了保护用户免受不良体验。在服务器上完成的请求验证仍处于打开状态,这是系统保护的一部分(在一定程度上它能够做到这一点)。

我之所以在这里说“部分”,是因为我听说内置的请求验证可能还不够,所以可能需要其他补充手段来提供充分的保护。但是,我这里介绍的javascript函数与保护系统无关。这只是为了确保用户不会有糟糕的体验。

你可以在这里试试:

函数xssCheckValidates(){var有效=真;var inp=document.querySelectorAll(“输入:not(:禁用):not([readonly]):not([type=hidden])”+“,textarea:not(:禁用):not([readonly])”);对于(变量i=0;i<inp.length;i++){if(!inp[i].readOnly){如果(inp[i].value.indexOf('<')>-1){有效=假;打破}如果(inp[i].value.indexOf('&#')>-1){有效=假;打破}}}if(有效){返回true;}其他{alert('在一个或多个文本字段中,您键入了\r\n字符“<”或字符序列“&#”。\r\n\r\n遗憾的是,这是不允许的,因为它可用于黑客尝试。\r\n\r\n请编辑字段并重试。');return false;}}<form onsubmit=“return xssCheckValidates();”>尝试键入<或&#<br/><input-type=“text”/><br/><textarea></textarea><input-type=“submit”value=“Send”/></form>

在我的例子中,使用asp:Textbox控件(asp.net4.5),而不是设置validateRequest=“false”的所有页面我用过

<asp:TextBox runat="server" ID="mainTextBox"
            ValidateRequestMode="Disabled"
 ></asp:TextBox>

在导致异常的文本框上。