ojek
ojek

Reputation: 10088

How do I avoid a potentially dangerous Request.Form value?

First of all, I know that there are dozens of questions similar to mine, but I didn't find any solution that would satisfy me...

So, I am getting the above exception on this code:

var returnUrl = Request.Params["returnUrl"] ?? Request.RawUrl;

After I submitted a post to my website, the mvc started preparing response page, and it crashes on the above code. Now, both of those variables do not contain any code that would cause this exception. Somehow MVC detects that I am using Request objects and throws an error without looking what am I doing. I don't want disabling those safety checks, but I don't know what else can I do?

Upvotes: 2

Views: 1827

Answers (3)

pwhe23
pwhe23

Reputation: 1334

I needed to access the unvalidated Request.Params in a ActionFilterAttribute and ended up writing this extension method that might help someone else. It should return essentially the same data as Request.Params collection but unvalidated. I am returning a dictionary but you could easily change that to a NameValueCollection if you prefer. This extension method is off of the request so I can access the ServerVariables collection since that is not available in the UnvalidatedRequestValuesBase class but is normally included in Params. It also does not support NameValueCollection.Values but only returns a single value which is slightly different than Params.

public static IReadOnlyDictionary<string, string> UnvalidatedParams(this HttpRequestBase request)
{
    var dict = new Dictionary<string, string>();
    var unvalidated = request.Unvalidated;

    for (var i = 0; i < unvalidated.QueryString.Count; i++)
    {
        var name = unvalidated.QueryString.GetKey(i);
        if (string.IsNullOrWhiteSpace(name))
            continue;

        var value = unvalidated.QueryString.Get(i);
        dict[name] = value;
    }

    for (var i = 0; i < unvalidated.Form.Count; i++)
    {
        var name = unvalidated.Form.GetKey(i);
        if (string.IsNullOrWhiteSpace(name))
            continue;

        var value = unvalidated.Form.Get(i);
        dict[name] = value;
    }

    var cookieCount = unvalidated.Cookies.Count;
    for (var i = 0; i < cookieCount; i++)
    {
        var cookie = unvalidated.Cookies[i];
        if (string.IsNullOrWhiteSpace(cookie?.Name))
            continue;

        dict[cookie.Name] = cookie.Value;
    }

    for (var i = 0; i < request.ServerVariables.Count; i++)
    {
        var name = request.ServerVariables.GetKey(i);
        if (string.IsNullOrWhiteSpace(name))
            continue;

        var value = request.ServerVariables.Get(i);
        dict[name] = value;
    }

    return dict;
}

Upvotes: 0

Chris Pratt
Chris Pratt

Reputation: 239460

Had the same issue and just found the solution. When you access posted values from Request, the values are re-validated and for some reason [AllowHtml] is ignored in this particular validation (perhaps a bug, tested in MVC 5). After a little searching, I stumbled upon:

http://msdn.microsoft.com/en-us/library/hh882339(v=vs.110).aspx

Down the page a bit, under the section "Disabling Request Validation in ASP.NET MVC", it mentions Request.Unvalidated. This property returns the request object while bypassing validation, so that you can then get at a particular param without invoking validation.

So instead of using Request.Params["returnUrl"], use Request.Unvalidated["returnUrl"].

Upvotes: 7

Justin Self
Justin Self

Reputation: 6265

If you are binding the request to a model, you could add the AllowHtml attribute to the model property that needs it. That allows you to keep the protection on the rest of the model.

Upvotes: 1

Related Questions