402

Why is Json Request Behavior needed?

If I want to restrict the HttpGet requests to my action I can decorate the action with the [HttpPost] attribute

Example:

[HttpPost]
public JsonResult Foo()
{
    return Json("Secrets");
}

// Instead of:
public JsonResult Foo()
{
    return Json("Secrets", JsonRequestBehavior.AllowGet);
}

Why isn't [HttpPost]sufficient?
Why the framework "bugs" us with the JsonRequestBehavior.AllowGet for every JsonResult that we have. If I want to deny get requests I'll add the HttpPost attribute.

gdoron is supporting Monica
  • 136,782
  • 49
  • 273
  • 342
  • Very similar to http://stackoverflow.com/questions/1625671/why-are-get-requests-returning-json-disallowed-by-default (although I found this one searching for my own question :)) – Jedidja Mar 12 '14 at 09:18
  • Because GET is supposed to be idempotent whereas POST is not. By making GETs -> POSTs, you change the semantics of the interface. – rism May 18 '15 at 01:14
  • 22
    Because your code would look too clean if you didn't have to add crufty args everywhere. – John Shedletsky Jun 12 '15 at 00:20

5 Answers5

283

MVC defaults to DenyGet to protect you against a very specific attack involving JSON requests to improve the liklihood that the implications of allowing HTTP GET exposure are considered in advance of allowing them to occur.

This is opposed to afterwards when it might be too late.

Note: If your action method does not return sensitive data, then it should be safe to allow the get.

Further reading from my Wrox ASP.NET MVC3 book

By default, the ASP.NET MVC framework does not allow you to respond to an HTTP GET request with a JSON payload. If you need to send JSON in response to a GET, you'll need to explicitly allow the behavior by using JsonRequestBehavior.AllowGet as the second parameter to the Json method. However, there is a chance a malicious user can gain access to the JSON payload through a process known as JSON Hijacking. You do not want to return sensitive information using JSON in a GET request. For more details, see Phil's post at http://haacked.com/archive/2009/06/24/json-hijacking.aspx/ or this SO post.

Haack, Phil (2011). Professional ASP.NET MVC 3 (Wrox Programmer to Programmer) (Kindle Locations 6014-6020). Wrox. Kindle Edition.

Related StackOverflow question

With most recents browsers (starting with Firefox 21, Chrome 27, or IE 10), this is no more a vulnerability.

G-Wiz
  • 7,141
  • 1
  • 31
  • 46
danludwig
  • 45,241
  • 21
  • 150
  • 230
  • 22
    But the question remains: Why isn't [HttpPost] sufficient? – gdoron is supporting Monica Dec 11 '11 at 14:30
  • 4
    I think it is sufficient. You only need AllowGet when you want to allow the data to pass as the result of a HttpGet. DenyGet is the default, if you invoke Json(data) with 1 parameter. – danludwig Dec 11 '11 at 14:36
  • 11
    This is my question. Why the framework "bugs" us with the `JsonRequestBehavior.AllowGet` for every JsonResult that I have. If I want to deny get request I'll add the `HttpPost` attribute. – gdoron is supporting Monica Dec 11 '11 at 14:38
  • 35
    I think it's because not a lot of people are aware of this obscure vulnerability. You say if you want to deny the request, you will do it with [HttpPost]. However the MVC authors are giving you a layer of protection out of the box against this kind of attack. Since you need to make effort to add the 2nd argument, you should take that time to consider what data you are exposing, and how sensitive it is. – danludwig Dec 11 '11 at 14:42
  • 11
    So now we clutter up our API and add verb confusion to "RESTful" interfaces to get around a potential CLIENT driven vulnerability? This seems terrible...but I appreciate the discussion. – Norman H Apr 30 '13 at 19:34
  • How is GET more vulnerable than POST? – Anders Lindén Jul 01 '16 at 10:47
  • 2
    @AndersLindén because script tags that point to a URL don't load those remote scripts via POST, so the attack would not succeed because the server only accepts POST requests for that data. – MarioDS Jul 06 '16 at 09:30
  • 1
    I think the fact that Microsoft protects programmers this way out of the box is certainly not a bad thing, but the *way* they've done it is rather annoying. The second argument is a rather lengthy enum member and it clutters code. They could easily have made an overload that accepts a `bool`, or even another method like `JsonGet(...)` for example. People would still wonder why it's there and learn about the vulnerability / risk. Sure, you can make those yourself on a BaseController, but I don't like having a BaseController at all personally. – MarioDS Jul 06 '16 at 09:34
  • If you're annoyed just subclass it –  Mar 14 '17 at 00:53
  • 1
    Ironically, modern browsers are not longer vulnerable to the JSON hijacking issue that prompted the MVC team to include that safeguard. See http://stackoverflow.com/questions/16289894/is-json-hijacking-still-an-issue-in-modern-browsers. – Jordan Rieger Mar 15 '17 at 21:14
60

To make it easier for yourself you could also create an actionfilterattribute

public class AllowJsonGetAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var jsonResult = filterContext.Result as JsonResult;

        if (jsonResult == null)
            throw new ArgumentException("Action does not return a JsonResult, 
                                                   attribute AllowJsonGet is not allowed");

        jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;            

        base.OnResultExecuting(filterContext);
    }
}

and use it on your action

[AllowJsonGet]
public JsonResult MyAjaxAction()
{
    return Json("this is my test");
}
radbyx
  • 8,605
  • 18
  • 75
  • 119
Arjen de Mooij
  • 601
  • 5
  • 5
  • 4
    Additionally, you could set this as default filter in RegisterGlobalFilters: filters.Add(new AllowJsonGetAttribute()). But then you have to remove the Exception since the Filter will be applied for all action-methods. – Vortex852456 Jan 26 '18 at 08:54
8

By default Jsonresult "Deny get"

Suppose if we have method like below

  [HttpPost]
 public JsonResult amc(){}

By default it "Deny Get".

In the below method

public JsonResult amc(){}

When you need to allowget or use get ,we have to use JsonRequestBehavior.AllowGet.

public JsonResult amc()
{
 return Json(new Modle.JsonResponseData { Status = flag, Message = msg, Html = html }, JsonRequestBehavior.AllowGet);
}
woggles
  • 7,054
  • 11
  • 65
  • 124
Deepakmahajan
  • 846
  • 1
  • 10
  • 23
6

Improving upon the answer of @Arjen de Mooij a bit by making the AllowJsonGetAttribute applicable to mvc-controllers (not just individual action-methods):

using System.Web.Mvc;
public sealed class AllowJsonGetAttribute : ActionFilterAttribute, IActionFilter
{
    void IActionFilter.OnActionExecuted(ActionExecutedContext context)
    {
        var jsonResult = context.Result as JsonResult;
        if (jsonResult == null) return;

        jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var jsonResult = filterContext.Result as JsonResult;
        if (jsonResult == null) return;

        jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
        base.OnResultExecuting(filterContext);
    }
}
Jake
  • 6,855
  • 6
  • 53
  • 64
XDS
  • 2,628
  • 1
  • 25
  • 40
3

You do not need it.

If your action has the HttpPost attribute, then you do not need to bother with setting the JsonRequestBehavior and use the overload without it. There is an overload for each method without the JsonRequestBehavior enum. Here they are:

Without JsonRequestBehavior

protected internal JsonResult Json(object data);
protected internal JsonResult Json(object data, string contentType);
protected internal virtual JsonResult Json(object data, string contentType, Encoding contentEncoding);

With JsonRequestBehavior

protected internal JsonResult Json(object data, JsonRequestBehavior behavior);
protected internal JsonResult Json(object data, string contentType, 
                                   JsonRequestBehavior behavior);
protected internal virtual JsonResult Json(object data, string contentType, 
    Encoding contentEncoding, JsonRequestBehavior behavior);
CodingYoshi
  • 21,881
  • 3
  • 45
  • 51