ASP.NET MVC Se detectó un valor Request.Form potencialmente peligroso del cliente cuando se usaba un encuadernador de modelos personalizado

95

Obteniendo el error aquí:

ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage");

¿Cómo permito solo una selección de valores? es decir

[ValidateInput(false)]
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage");
    ValueProviderResult value2 = bindingContext.ValueProvider.GetValue("ConfirmationMessage2");
}
DW
fuente
1
Se detectó un posible duplicado de un valor de Request.Form potencialmente peligroso desde el cliente , no importa si se trata de Webforms o MVC.
Erik Philips
2
Gracias, pero no ha visto mi problema como diferente
DW
El mismo problema raíz exacto, la única diferencia es que puede haber formas específicas de MVC para tratarlo.
Erik Philips
Cuando use EF, vea la respuesta de bizzehdee aquí stackoverflow.com/questions/17964313/…
Petr

Respuestas:

223

Tienes pocas opciones.

En el modelo, agregue este atributo a cada propiedad que necesite para permitir HTML: la mejor opción

using System.Web.Mvc;

[AllowHtml]
public string SomeProperty { get; set; }

En la acción del controlador, agregue este atributo para permitir todo HTML

[ValidateInput(false)]
public ActionResult SomeAction(MyViewModel myViewModel)

Fuerza bruta en web.config: definitivamente no se recomienda

En el archivo web.config, dentro de las etiquetas, inserte el elemento httpRuntime con el atributo requestValidationMode = "2.0". También agregue el atributo validateRequest = "false" en el elemento de páginas.

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

Más información: http://davidhayden.com/blog/dave/archive/2011/01/16/AllowHtmlAttributeASPNETMVC3.aspx

Lo anterior funciona para usos del encuadernador de modelos predeterminado.

ModelBinder personalizado

Parece que una llamada a bindingContext.ValueProvider.GetValue () en el código anterior siempre valida los datos, independientemente de los atributos. Profundizar en las fuentes de ASP.NET MVC revela que DefaultModelBinder primero verifica si se requiere la validación de la solicitud y luego llama al método bindingContext.UnvalidatedValueProvider.GetValue () con un parámetro que indica si la validación es necesaria o no.

Desafortunadamente, no podemos usar ninguno de los códigos del marco porque está sellado, es privado o lo que sea para proteger a los desarrolladores ignorantes de hacer cosas peligrosas, pero no es demasiado difícil crear una carpeta de modelos personalizada que funcione y respete los atributos AllowHtml y ValidateInput:

public class MyModelBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // First check if request validation is required
        var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled;

        // Get value
        var valueProviderResult = bindingContext.GetValueFromValueProvider(shouldPerformRequestValidation);
        if (valueProviderResult != null)
        {
            var theValue = valueProviderResult.AttemptedValue;

            // etc...
        }
    }
}

La otra pieza requerida es una forma de recuperar un valor no validado. En este ejemplo usamos un método de extensión para la clase ModelBindingContext:

public static class ExtensionHelpers
{
    public static ValueProviderResult GetValueFromValueProvider(this ModelBindingContext bindingContext, bool performRequestValidation)
    {
        var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
        return (unvalidatedValueProvider != null)
          ? unvalidatedValueProvider.GetValue(bindingContext.ModelName, !performRequestValidation)
          : bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    }
}

Más información sobre esto en http://blogs.taiga.nl/martijn/2011/09/29/custom-model-binders-and-request-validation/

ericdc
fuente
tengo esto en el controlador [HttpPost, ValidateInput (false)] y todavía obtengo el error
DW
Vea mi respuesta revisada con una forma de evitar esto cuando use un encuadernador de modelos personalizado
ericdc
Gracias, pero no le gusta esta línea bindingContext.GetValueFromValueProvider
DW
GetValueFromValueProvider debe estar en una clase estática pública. Mira las ediciones anteriores.
ericdc
Ta, valueProviderResult vuelve nulo aunque? var valueProviderResult = bindingContext.GetValueFromValueProvider (shouldPerformRequestValidation);
DW
31

Tratar:

HttpRequestBase request = controllerContext.HttpContext.Request;
string re = request.Unvalidated.Form.Get("ConfirmationMessage")
DW
fuente
Cuando intento esto, obtengo una excepción que dice: El miembro no invocable 'System.web.HttpRequestBase.Unvalidated' no se puede usar como un método. ¿Ha cambiado esto?
Stack0verflow
7
La segunda línea debería ser realmentevar re = request.Unvalidated.Form["ConfirmationMessage"];
Stack0verflow
5

Ampliando la respuesta de @DW, en mi controlador de edición, al iterar sobre los valores del formulario, tuve que reemplazar todas las instancias de Request.Params.AllKeyscon Request.Unvalidated.Form.AllKeysy todas las instancias de Request[key]con Request.Unvalidated.Form[key].

Esta fue la única solución que funcionó para mí.

Mike Godin
fuente
0

Como escribió Mike Godin, incluso si establece el atributo [ValidateInput (false)], debe usar Request.Unvalidated.Form en lugar de Request.Form. Esto funcionó para mí con ASP.NET MVC 5

Ryozzo
fuente
1
Este fue en realidad un consejo útil, ya que acceder a los datos de un controlador base (es decir, para fines de registro o depuración) cualquier acceso a Request.Form arroja una excepción incluso si el modelo tiene este atributo.
nsimeonov
-5

Estos son los pasos para codificar a nivel de cliente y decodificarlo a nivel de servidor:

  1. Publique el formulario utilizando el método de envío jquery.

  2. En el botón jquery, haga clic en el campo de codificación del método de evento que desea publicar en el servidor. Ejemplo:

    $("#field").val(encodeURIComponent($("#field").val()))
    $("#formid").submit();
  3. En el nivel del controlador, acceda a todos los valores de identificación del formulario utilizando

    HttpUtility.UrlDecode(Request["fieldid"])

Ejemplo de muestra:

  • Nivel de controlador:

    public ActionResult Name(string id)
    {
    
        CheckDispose();
        string start = Request["start-date"];
        string end = Request["end-date"];
        return View("Index", GetACPViewModel(HttpUtility.UrlDecode(Request["searchid"]), start, end));
    }
  • Nivel de cliente:

    <% using (Html.BeginForm("Name", "App", FormMethod.Post, new { id = "search-form" }))
    { %>
    <div>
    <label  for="search-text" style="vertical-align: middle" id="search-label" title="Search for an application by name, the name for who a request was made, or a BEMSID">App, (For Who) Name, or BEMSID: </label>
    <%= Html.TextBox("searchid", null, new {value=searchText, id = "search-text", placeholder = "Enter Application, Name, or BEMSID" })%>
    </div>
    <div>
    <input id="start-date" name="start-date" class="datepicker" type="text"  placeholder="Ex: 1/1/2012"/>
    </div>
    <div>
    <input id="end-date" name="end-date" class="datepicker" type="text"  placeholder="Ex: 12/31/2012"/>
    </div>
    <div>
    <input type="button" name="search" id="btnsearch" value="Search" class="searchbtn" style="height:35px"/>
    </div> 
    <% } %>

En la función Documento listo:

$(function () {     
  $("#btnsearch").click(function () {  
    $("#search-text").val(encodeURIComponent($("#search-text").val()));
    $("#search-form").submit();
  });
});
Prakash Rajendran
fuente
4
Jquery y la tecnología del lado del cliente no tienen nada que ver con MVC, la validación ocurre en el lado del servidor con el marco MVC. No es una respuesta válida
diegosasw
1
Dado que Microsoft literalmente ignora el atributo AllowHtml, y dado que la única solución viable del lado del servidor es reemplazar la funcionalidad del enlazador de modelos predeterminado, yo diría que la codificación del lado del cliente es una opción perfectamente válida.
Jonathan