ASP.NET MVC Une valeur Request.Form potentiellement dangereuse a été détectée à partir du client lors de l'utilisation d'un modelbinder personnalisé

95

Obtenir l'erreur ici:

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

Comment autoriser une sélection de valeurs uniquement? c'est à dire

[ValidateInput(false)]
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage");
    ValueProviderResult value2 = bindingContext.ValueProvider.GetValue("ConfirmationMessage2");
}
DW
la source
1
Une duplication possible d' une valeur Request.Form potentiellement dangereuse a été détectée à partir du client , peu importe qu'il s'agisse de Webforms ou de MVC.
Erik Philips
2
Merci, mais vous n'avez pas considéré mon problème comme étant différent
DW
Même problème de racine exact, la seule différence est qu'il peut y avoir des moyens spécifiques à MVC pour le résoudre.
Erik Philips
Lorsque vous utilisez EF, voir la réponse bizzehdee ici stackoverflow.com/questions/17964313/…
Petr

Réponses:

223

Vous avez quelques options.

Sur le modèle, ajoutez cet attribut à chaque propriété dont vous avez besoin pour autoriser HTML - meilleur choix

using System.Web.Mvc;

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

Sur l'action du contrôleur, ajoutez cet attribut pour autoriser tout HTML

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

Force brute dans web.config - certainement pas recommandé

Dans le fichier web.config, dans les balises, insérez l'élément httpRuntime avec l'attribut requestValidationMode = "2.0". Ajoutez également l'attribut validateRequest = "false" dans l'élément pages.

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

Plus d'infos: http://davidhayden.com/blog/dave/archive/2011/01/16/AllowHtmlAttributeASPNETMVC3.aspx

Ce qui précède fonctionne pour les utilisations du modelbinder par défaut.

ModelBinder personnalisé

Il semble qu'un appel à bindingContext.ValueProvider.GetValue () dans le code ci-dessus valide toujours les données, quels que soient les attributs. L'exploration des sources ASP.NET MVC révèle que DefaultModelBinder vérifie d'abord si la validation de la demande est requise, puis appelle la méthode bindingContext.UnvalidatedValueProvider.GetValue () avec un paramètre qui indique si la validation est requise ou non.

Malheureusement, nous ne pouvons utiliser aucun code du framework car il est scellé, privé ou autre pour protéger les développeurs ignorants de faire des choses dangereuses, mais il n'est pas trop difficile de créer un classeur de modèle personnalisé fonctionnel qui respecte les attributs AllowHtml et 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...
        }
    }
}

L'autre élément requis est un moyen de récupérer une valeur non validée. Dans cet exemple, nous utilisons une méthode d'extension pour la classe 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);
    }
}

Plus d'informations à ce sujet sur http://blogs.taiga.nl/martijn/2011/09/29/custom-model-binders-and-request-validation/

ericdc
la source
J'ai ceci sur le contrôleur [HttpPost, ValidateInput (false)] et j'obtiens toujours l'erreur
DW
Voir ma réponse révisée avec un moyen de contourner ce problème lorsque vous utilisez un classeur de modèles personnalisé
ericdc
Merci, mais il n'aime pas cette ligne bindingContext.GetValueFromValueProvider
DW
GetValueFromValueProvider doit être dans une classe statique publique. Consultez les modifications ci-dessus.
ericdc
Ta, valueProviderResult reutrns null tho? var valueProviderResult = bindingContext.GetValueFromValueProvider (shouldPerformRequestValidation);
DW
31

Essayer:

HttpRequestBase request = controllerContext.HttpContext.Request;
string re = request.Unvalidated.Form.Get("ConfirmationMessage")
DW
la source
Quand j'essaye ceci, j'obtiens une exception qui dit: Le membre non invocable «System.web.HttpRequestBase.Unvalidated» ne peut pas être utilisé comme une méthode. Cette chose a-t-elle changé?
Stack0verflow
7
La deuxième ligne devrait vraiment êtrevar re = request.Unvalidated.Form["ConfirmationMessage"];
Stack0verflow
5

En développant la réponse de @DW, dans mon contrôleur d'édition, en itérant sur les valeurs de formulaire, j'ai dû remplacer toutes les instances de Request.Params.AllKeysavec Request.Unvalidated.Form.AllKeyset toutes les instances de Request[key]avec Request.Unvalidated.Form[key].

C'était la seule solution qui fonctionnait pour moi.

Mike Godin
la source
0

Comme l'a écrit Mike Godin, même si vous définissez l'attribut [ValidateInput (false)], vous devez utiliser Request.Unvalidated.Form au lieu de Request.Form Cela a fonctionné pour moi avec ASP.NET MVC 5

Ryozzo
la source
1
C'était en fait un conseil utile, car l'accès aux données d'un contrôleur de base (c'est-à-dire à des fins de journalisation ou de débogage) tout accès à Request.Form lève une exception même si le modèle a cet attribut.
nsimeonov le
-5

Voici les étapes pour encoder au niveau du client et le décoder au niveau du serveur:

  1. Publiez le formulaire en utilisant la méthode d'envoi jquery.

  2. Dans le bouton jquery, cliquez sur la méthode d'événement pour encoder le champ que vous souhaitez publier sur le serveur. Exemple:

    $("#field").val(encodeURIComponent($("#field").val()))
    $("#formid").submit();
  3. Au niveau du contrôleur, accédez à toutes les valeurs d'identifiant de formulaire en utilisant

    HttpUtility.UrlDecode(Request["fieldid"])

Exemple d'exemple:

  • Niveau du contrôleur:

    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));
    }
  • Niveau client:

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

Dans la fonction Document Ready:

$(function () {     
  $("#btnsearch").click(function () {  
    $("#search-text").val(encodeURIComponent($("#search-text").val()));
    $("#search-form").submit();
  });
});
Prakash Rajendran
la source
4
La technologie Jquery et côté client n'a rien à voir avec MVC, la validation se fait côté serveur avec le framework MVC. Ce n'est pas une réponse valide
diegosasw
1
Étant donné que Microsoft ignore littéralement l'attribut AllowHtml et étant donné que la seule solution viable côté serveur est de remplacer la fonctionnalité du classeur de modèle par défaut, je dirais que l'encodage côté client est une option parfaitement valide.
Jonathan