От клиента было обнаружено потенциально опасное значение Request.Form - PullRequest
1397 голосов
/ 17 сентября 2008

Каждый раз, когда пользователь публикует что-то, содержащее < или > на странице в моем веб-приложении, я получаю это исключение.

Я не хочу вдаваться в дискуссию о целесообразности создания исключения или сбоя всего веб-приложения, потому что кто-то ввел символ в текстовое поле, но я ищу элегантный способ справиться с этим. *

Перехват исключения и отображение

Произошла ошибка. Вернитесь и введите заново всю форму, но на этот раз, пожалуйста, не используйте <</p>

мне не кажется достаточно профессиональным.

Отключение проверки сообщений (validateRequest="false") определенно позволит избежать этой ошибки, но сделает страницу уязвимой для ряда атак.

В идеале: когда происходит обратная запись, содержащая ограниченные символы HTML, это опубликованное значение в коллекции Form будет автоматически закодировано в HTML. Таким образом, свойство .Text моего текстового поля будет something & lt; html & gt;

Есть ли способ сделать это из обработчика?

Ответы [ 43 ]

3 голосов
/ 25 февраля 2016

Вы можете автоматически кодировать 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);
        }
    }

В Application_Start:

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;
        }
    }
}

ErrorController:

public class ErrorController : Controller
 {
   public ActionResult InvalidUrl()
   {
      return View();
   }
}   
2 голосов
/ 04 мая 2018

Я знаю, что этот вопрос касается публикации формы, но я хотел бы добавить некоторые подробности для людей, которые получили эту ошибку при других обстоятельствах. Это также может произойти в обработчике, используемом для реализации веб-службы.

Предположим, что ваш веб-клиент отправляет запросы POST или PUT, используя ajax, и отправляет в веб-службу текст json или xml или необработанные данные (содержимое файла). Поскольку вашей веб-службе не требуется получать какую-либо информацию из заголовка Content-Type, ваш код JavaScript не установил этот заголовок для вашего запроса ajax. Но если вы не установите этот заголовок в запросе ajax POST / PUT, Safari может добавить этот заголовок: «Content-Type: application / x-www-form-urlencoded». Я заметил, что на Safari 6 на iPhone, но другие версии Safari / OS или Chrome могут делать то же самое. Таким образом, при получении этого заголовка Content-Type некоторая часть .NET Framework предполагает, что структура данных тела запроса соответствует публикации формы html, в то время как это не так, и возникла исключительная ситуация HttpRequestValidationException. Первое, что нужно сделать, это, очевидно, всегда устанавливать заголовок Content-Type на что-либо, кроме MIME-типа в форме запроса ajax POST / PUT, даже если это бесполезно для вашего веб-сервиса.

Я также обнаружил эту деталь:
При таких обстоятельствах возникает исключение HttpRequestValidationException, когда ваш код пытается получить доступ к коллекции HttpRequest.Params. Но, что удивительно, это исключение не возникает при доступе к коллекции HttpRequest.ServerVariables. Это показывает, что, хотя эти две коллекции кажутся почти идентичными, один обращается к данным запроса с помощью проверок безопасности, а другой - нет.

2 голосов
/ 24 июля 2018

Для тех, кто не использует привязку модели, кто извлекает каждый параметр из Request.Form, кто уверен, что входной текст не принесет вреда, есть другой способ. Не отличное решение, но оно сделает работу.

Со стороны клиента закодируйте его как uri, а затем отправьте.
например:

encodeURIComponent($("#MsgBody").val());  

Со стороны сервера примите его и декодируйте как uri.
например:

string temp = !string.IsNullOrEmpty(HttpContext.Current.Request.Form["MsgBody"]) ?
System.Web.HttpUtility.UrlDecode(HttpContext.Current.Request.Form["MsgBody"]) : 
null;  

или

string temp = !string.IsNullOrEmpty(HttpContext.Current.Request.Form["MsgBody"]) ?
System.Uri.UnescapeDataString(HttpContext.Current.Request.Form["MsgBody"]) : 
null; 

обратите внимание на различия между UrlDecode и UnescapeDataString

2 голосов
/ 17 сентября 2008

Вы должны использовать метод Server.HtmlEncode, чтобы защитить ваш сайт от опасного ввода.

Подробнее здесь

2 голосов
/ 02 мая 2014

Как указано в моем комментарии к ответу Села , это наше расширение для валидатора пользовательских запросов.

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);
    }
}
1 голос
/ 03 декабря 2018

в моем случае, используя элемент управления asp: Textbox (Asp.net 4.5) вместо установки всей страницы для validateRequest="false" я использовал

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

в текстовом поле, вызвавшем исключение.

1 голос
/ 04 апреля 2018

Решение

Мне не нравится отключать проверку сообщений (validateRequest = "false"). С другой стороны, недопустимо, чтобы приложение зависало только потому, что невинный пользователь набирает <x или что-то в этом роде.

Поэтому я написал функцию javascript на стороне клиента (xssCheckValidates), которая выполняет предварительную проверку. Эта функция вызывается при попытке опубликовать данные формы, например:

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

Функция довольно проста и может быть улучшена, но она выполняет свою работу.

Обратите внимание, что цель этого состоит не в том, чтобы защитить систему от взлома, а в том, чтобы защитить пользователей от плохого опыта. Проверка запроса, выполненная на сервере, все еще включена, и это (часть) защиты системы (в той степени, в которой она способна это сделать).

Причина, по которой я говорю здесь «часть», заключается в том, что я слышал, что встроенной проверки запросов может быть недостаточно, поэтому могут потребоваться другие дополнительные средства для обеспечения полной защиты. Но, опять же, функция javascript, которую я здесь представляю, не имеет ничего общего с защитой системы. Это только , предназначенное для того, чтобы убедиться, что пользователи не будут иметь плохой опыт.

Вы можете попробовать это здесь:

    function xssCheckValidates() {
      var valid = true;
      var inp = document.querySelectorAll(
          "input:not(:disabled):not([readonly]):not([type=hidden])" +
          ",textarea:not(:disabled):not([readonly])");
      for (var i = 0; i < inp.length; i++) {
        if (!inp[i].readOnly) {
          if (inp[i].value.indexOf('<') > -1) {
            valid = false;
            break;
          }
          if (inp[i].value.indexOf('&#') > -1) {
            valid = false;
            break;
          }
        }
      }
      if (valid) {
        return true;
      } else {
        alert('In one or more of the text fields, you have typed\r\nthe character "<" or the character sequence "&#".\r\n\r\nThis is unfortunately not allowed since\r\nit can be used in hacking attempts.\r\n\r\nPlease edit the field and try again.');
        return false;
      }
    }
<form onsubmit="return xssCheckValidates();" >
  Try to type < or &# <br/>
  <input type="text" /><br/>
  <textarea></textarea>
  <input type="submit" value="Send" />
</form>
1 голос
/ 15 марта 2018

В .Net 4.0 и более поздних версиях, что является обычным случаем, установите следующий параметр в system.web

<system.web>
     <httpRuntime requestValidationMode="2.0" />
1 голос
/ 27 января 2018

И последнее, но не менее важное: обратите внимание, что элементы управления привязки данных ASP.NET автоматически кодируют значения во время привязки данных. Это изменяет поведение по умолчанию всех элементов управления ASP.NET (TextBox, Label и т. Д.), Содержащихся в ItemTemplate. Следующий пример демонстрирует (ValidateRequest установлено в false):

ASPX

<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication17._Default" %> <html> <body>
    <form runat="server">
        <asp:FormView ID="FormView1" runat="server" ItemType="WebApplication17.S" SelectMethod="FormView1_GetItem">
            <ItemTemplate>
                <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
                <asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
                <asp:Label ID="Label1" runat="server" Text="<%#: Item.Text %>"></asp:Label>
                <asp:TextBox ID="TextBox2" runat="server" Text="<%#: Item.Text %>"></asp:TextBox>
            </ItemTemplate>
        </asp:FormView>
    </form> 

код позади

public partial class _Default : Page
{
    S s = new S();

    protected void Button1_Click(object sender, EventArgs e)
    {
        s.Text = ((TextBox)FormView1.FindControl("TextBox1")).Text;
        FormView1.DataBind();
    }

    public S FormView1_GetItem(int? id)
    {
        return s;
    }
}

public class S
{
    public string Text { get; set; }
}
  1. Значение для подачи дела: &#39;

Label1.Text значение: &#39;

TextBox2.Text значение: &amp;#39;

  1. Значение для представления дела: <script>alert('attack!');</script>

Label1.Text значение: <script>alert('attack!');</script>

TextBox2.Text значение: &lt;script&gt;alert(&#39;attack!&#39;);&lt;/script&gt;

1 голос
/ 16 января 2017

Для тех из нас, кто еще застрял на веб-формах, я нашел следующее решение, которое позволяет отключить проверку только в одном поле! (Я бы не хотел отключать его для всей страницы.)

VB.NET:

Public Class UnvalidatedTextBox
    Inherits TextBox
    Protected Overrides Function LoadPostData(postDataKey As String, postCollection As NameValueCollection) As Boolean
        Return MyBase.LoadPostData(postDataKey, System.Web.HttpContext.Current.Request.Unvalidated.Form)
    End Function
End Class

C #:

public class UnvalidatedTextBox : TextBox
{
    protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
    {
        return base.LoadPostData(postDataKey, System.Web.HttpContext.Current.Request.Unvalidated.Form);
    }
}

Теперь просто используйте <prefix:UnvalidatedTextBox id="test" runat="server" /> вместо <asp:TextBox, и он должен разрешать все символы (это идеально подходит для полей пароля!)

...