What triggers MVC JSON Get blocking - intermittently working - asp.net-mvc

I have a JSON method that accepts a GET request and returns a JSON object (not array). I'm aware of JSON Hijacking and the implications. I've read the Phil Haack post. The problem is that the method works 98% of the time for GET and POST. The other times I'm recording this error:
This request has been blocked because sensitive information could be disclosed to
third party web sites when this is used in a GET request. To allow GET requests, set
JsonRequestBehavior to AllowGet.
My method is simple and takes a single integer parameter...
[Authorize]
public ActionResult MyMediaJSON(int? id) {
<get data & return result>
}
What conditions trigger the message? What should look for as I debug this?

I've just looked at the MVC source code and it do not add up with what you are saying in your question.
To me it looks like JsonRequestBehavior.DenyGet is used for all JSON results per default. Hence you should get the error message each time you try to return JSON from a controller using a GET request (without specifying JsonRequestBehavior.AllowGet).
The actual control is done in JsonResult.ExecuteResult and looks like:
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) {
throw new InvalidOperationException(MvcResources.JsonRequest_GetNotAllowed);
}
What conditions trigger the message? What should look for as I debug this?
Any actions that are getting invoked through GET that returns JsonResult without specifying JsonRequestBehavior.AllowGet. (the Json method in the controller uses JsonResult)

Related

Previous GET parameters being unintentionally preserved in POST request

As of current I navigate to a view using a GET request, looking something like this:
/batches/install?Id=2&ScheduledDate=07.29%3A12
From there, I send a POST request using a form (where I include what data I wish to include in the request.
Furthermore I set the forms action to "Create" which is the action I wish to send the request to.
My issue is the fact that sending this request keeps the GET arguments in the POST url, making it look the following:
../batches/Create/2?ScheduledDate=07.29%3A12
I do not want this since:
1: it looks weird
2: it sends data I do not intend it to send in this request
3: if my model already has a property named "id" or "scheduledDate" the unintentional GET parameters will get bound to those properties.
How can I ignore the current GET parameters in my new POST request?
I just want to send the form POST data to the url:
../batches/create
(without any GET parameters)
How would this be done?
As requested, here is my POST form:
#using (var f = Html.Bootstrap().Begin(new Form("Create")))
{
//inputs omitted for brevity
#Html.Bootstrap().SubmitButton().Style(ButtonStyle.Success).Text("Create batch")
}
Note that I use the TwitterBootstrapMVC html helpers (https://www.twitterbootstrapmvc.com/), althought this really shouldn't matter.
As requested, to sum up:
I send a get request to : /batches/install?Id=2&ScheduledDate=07.29%3A12.
Through the returned view I send a POST request to: /batches/create.
However the previous get parameters get included in the POST request URL making the POST request query: /batches/Create/2?ScheduledDate=07.29%3A12 (which is NOT intended).
This is not a great Idea though, but will give you what you want.
[HttpPost]
public ActionResult Create() {
//do whatever you want with the values
return RedirectToAction("Create", "batches");
}
This is a "Feature" of MVC that was previously reported as a bug (issue 1346). As pointed out in the post, there are a few different workarounds for it:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
For this particular scenario, you could just explicitly declare the parameters with an empty string inside of the Form.
#using (var f = Html.Bootstrap().Begin(new Form("Create").RouteValues(new { Id = "", ScheduledDate = "" })))
{
//inputs omitted for brevity
#Html.Bootstrap().SubmitButton().Style(ButtonStyle.Success).Text("Create batch")
}

What 'sensitive information' could be disclosed when setting JsonRequestBehavior to AllowGet

I've been getting the same old error every time I test a new URL from my browser's address bar when I'm returning Json (using the built-in MVC JsonResult helper):
This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.
Rather than grunt in acknowledgement and fire up Fiddler to do a post request, this time, I'm wondering exactly what it is that a GET request exposes that a POST request doesn't?
in your return use the following:
return this.Json("you result", JsonRequestBehavior.AllowGet);
Say your website has a GetUser web method:
http://www.example.com/User/GetUser/32
which returns a JSON response:
{ "Name": "John Doe" }
If this method accepts only POST requests, then the content will only be returned to the browser if an AJAX request is made to http://www.example.com/User/GetUser/32 using the POST method. Note that unless you have implemented CORS, the browser will protect the data from other domains making this request to yours.
However, if you allowed GET requests then as well as making an AJAX request similar to the above with GET instead of POST, a malicious user could include your JSON in the context of their own site by using a script tag in the HTML. e.g. on www.evil.com:
<script src="http://www.example.com/User/GetUser/32"></script>
This JavaScript should be useless to www.evil.com because there should be no way of reading the object returned by your web method. However, due to bugs in old versions of browsers (e.g. Firefox 3), it is possible for JavaScript prototype objects to be redefined and make it possible for www.evil.com to read your data returned by your method. This is known as JSON Hijacking.
See this post for some methods of preventing this. However, it is not a known problem with the later versions of modern browsers (Firefox, Chrome, IE).
By default, the ASP.NET MVC framework does not allow you to respond to
a GET request with a JSON payload as there is a chance a malicious user can gain access to the payload through a process known as JSON Hijacking. You do not want to return sensitive information using JSON in a GET request.
If you need to send JSON in response to a GET, and aren't exposing sensitive data, you can explicitly allow the behavior by passing JsonRequestBehavior.AllowGet as a second parameter to the Json
method.
Such as
[HttpGet] //No need to decorate, as by default it will be GET
public JsonResult GetMyData(){
var myResultDataObject = buildMyData(); // build, but keep controller thin
// delegating buildMyData to builder/Query Builder using CQRS makes easy :)
return Json(myResultDataObject, JsonRequestBehavior.AllowGet);
}
Here is an interesting article from Phil Haack JSON Hijacking about why not to use Json with GET method
When we want to return a json object to client from MVC application, we should explicit specify JsonRequestBehavior.AllowGet when returning an object. As a result, I return json data as below to overcome the issue:
return Json(yourObjectData, JsonRequestBehavior.AllowGet);
You must be use JsonRequestBehavior.AllowGet for Json Response like this :
return Json(YourObject, JsonRequestBehavior.AllowGet);
return Json("Success", JsonRequestBehavior.AllowGet)

How can I force asp.net webapi to always decode POST data as JSON

I am getting some json data posted to my asp.net webapi, but the post parameter is always coming up null - the data is not being serialized correctly. The method looks something like this:
public HttpResponseMessage Post(string id, RegistrationData registerData)
It seems the problem is that the client (which I have no control over) is always sending the content-type as x-www-form-urlencoded, even though the content is actually json. This causes mvc to try to deserialize it as form data, which fails.
Is there any way to get webapi to always deserialize as json, and to ignore the content-type header?
I found the answer here: http://blog.cdeutsch.com/2012/08/force-content-types-to-json-in-net.html
This code needs to be added to Application_Start or WebApiConfig.Register
foreach (var mediaType in config.Formatters.FormUrlEncodedFormatter.SupportedMediaTypes)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(mediaType);
}
config.Formatters.Remove(config.Formatters.FormUrlEncodedFormatter);
config.Formatters.Remove(config.Formatters.XmlFormatter);
It tells the json formatter to accept every type, and removes the form and xml formatters
I would suggest to rather modify the incoming request's content-type, let's say at the message handler to the appropriate content-type, rather than removing the formatters from config

Request.Form not populated when using the HTTP PUT method (ASP.NET MVC)

I'm attempting to process the body of an HTTP PUT request, but it seems that the MVC engine (or perhaps the ASP.NET stack underpinning it) isn't automatically parsing & populating the request's Form collection with the body data.
This does work as expected when doing a POST.
Note that the request's InputStream property does contain the expected data, and obviously I can build my own key/value collection using that, however I would have expected PUT to work the same way as a POST.
Am I missing something here?
Example action method:
[AcceptVerbs(HttpVerbs.Put)]
public ActionResult Purchase(int id, FormCollection data)
{
// Do stuff with data, except the collection is empty (as is Request.Form)
}
Quote from the doc:
The Form collection retrieves the
values of form elements posted to the
HTTP request body, with a form using
the POST method.
So instead of using Request.Form I would recommend you writing a custom model class that will hold the request data and pass it as action parameter. The default model binder will automatically populate the properties from the key/values passed in the request stream:
[AcceptVerbs(HttpVerbs.Put)]
public ActionResult Purchase(MyCustomModel model)
{
// Do stuff with the model
}
Asp.net does not support PUT out of the box for custom requests. If you are using not the built in capabilities to generate the PUT url try adding X-HTTP-Method-Override with value of PUT in headers, form, or query string.

How to simiulate a JSON call from an HTTP GET Call?

At this moment I am calling my index view in one of two ways. Using the normal http view and serializing it JSON. In order to test it I am using the code below, and it works. I want to get it with a http get call. Like (http://localhost/article,json or something similar. Any ideas.
$.getJSON("/Article", function(json) {
$.each(json, function(i, article) {
alert(article.title);
});
});
At this moment the index called to /article is being differentiated with the following IsAjaxRequest method. but my real question is if I am able to get around the .getJSON method in JQuery to test the following code.
if (Request.IsAjaxRequest()) {
return Json(articles);
} else {
return View(articles);
}
If you are trying to reuse the same action method for both a full GET (the page load) and an AJAX call (via getJSON), you'll run into issues because each action method should have a unique name. Otherwise, the MVC engine can't tell which action method should be called when a particular Url is requested.
You'll need two separate actions: one for the full page load that returns a ViewResult and the other for the AJAX call that returns a JsonResult. If you need the Urls for these actions to look the same, you can also play around with mapped routes that direct to different action methods.
So, how about:
/Article/Index
Maps to the default Index action (full page load)
/Article/Refresh
Maps to the Refresh action (asynchronous JSON call)
I'm not sure I understand the question correctly, but can't you make an optional parameter called "format", so that you pass in ?format=json to switch what reply type you get back?
if(Request.IsAjaxRequest() || (!string.IsNullOrEmpty(format) && format.Equals("json"))
{
...
}
If you're wondering how to test your action and you're talking about doing automated testing, see if this post answers your question.

Resources