Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the security reasons for not allowing get requests with MVC/ajax? [duplicate]

As part of the ASP.NET MVC 2 Beta 2 update, JSON GET requests are disallowed by default. It appears that you need to set the JsonRequestBehavior field to JsonRequestBehavior.AllowGet before returning a JsonResult object from your controller.

public JsonResult IsEmailValid(...)
{
    JsonResult result = new JsonResult();

    result.Data = ..... ;
    result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;

    return result;
}

What is the reasoning behind this? If I am using JSON GET to try and do some remote validation, should I be using a different technique instead?

like image 433
Jedidja Avatar asked Oct 26 '09 16:10

Jedidja


People also ask

Can we use Ajax in MVC?

The MVC Framework contains built-in support for unobtrusive Ajax. You can use the helper methods to define your Ajax features without adding a code throughout all the views. This feature in MVC is based on the jQuery features. To enable the unobtrusive AJAX support in the MVC application, open the Web.

Why We Need Ajax call in MVC?

It is a client-side script that communicates to and from a server/database without the need for a postback or a complete page refresh. The Ajax speeds up response time. In other words, Ajax is the method of exchanging data with a server, and updating parts of a web page, without reloading the entire page.

What is cross-site request forgery in MVC?

Cross-Site Request Forgery (CSRF) is a process in which a user first signs on to a genuine website (e.g. facebook.com) and after successful login, the user opens another website (malicious website) in the same browser. Both websites are opened in the same browser.


4 Answers

I also had your problem when I migrated my MVC website from Visual Studio 2008 to Visual Studio 2010.

The main aspx is below, it has an ViewData which calls a Category Controller in order to fill up ViewData["Categories"] with SelectList collection. There's also a script to call a Subcategory Controller to fill up the second combo with javascript. Now I was able to fix it adding up AlloGet attribute on this second controller.

Here's the aspx and javascript

<head>
<script type="text/javascript" src="../../Scripts/jquery-1.4.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#CategoryId").change(function () {

    var categoryId = $(this)[0].value;

    $("#ctl00_MainContent_SubcategoryId").empty();
    $("#ctl00_MainContent_SubcategoryId").append("<option value=''>-- select a category --</option>");
    var url = "/Subcategory/Subcategories/" + categoryId;

    $.getJSON(url, { "selectedItem": "" }, function (data) {
        $.each(data, function (index, optionData) {
            $("#ctl00_MainContent_SubcategoryId").append("<option value='" + optionData.SubcategoryId + "'>" + optionData.SubcategoryName + "</option>");
        });
        //feed our hidden html field
        var selected = $("#chosenSubcategory") ? $("#chosenSubcategory").val() : '';
        $("#ctl00_MainContent_SubcategoryId").val(selected);

    });

}).change();
});
</script>
<body>
<% using (Html.BeginForm()) {%>
<label for="CategoryId">Category:</label></td>
<%= Html.DropDownList("CategoryId", (SelectList)ViewData["Categories"], "--categories--") %>
<%= Html.ValidationMessage("category","*") %>
<br/>
<label class="formlabel" for="SubcategoryId">Subcategory:</label><div id="subcategoryDiv"></div>
<%=Html.Hidden("chosenSubcategory", TempData["subcategory"])%>
<select id="SubcategoryId" runat="server">
</select><%= Html.ValidationMessage("subcategory", "*")%>
<input type="submit" value="Save" />
<%}%>                

here's my controller for subcategories

public class SubcategoryController : Controller
{
    private MyEntities db = new MyEntities();

    public int SubcategoryId { get; set; }
    public int SubcategoryName { get; set; }
    public JsonResult Subcategories(int? categoryId)
    {
        try
        {
            if (!categoryId.HasValue)
                categoryId = Convert.ToInt32(RouteData.Values["id"]);
            var subcategories = (from c in db.Subcategories.Include("Categories")
                                 where c.Categories.CategoryId == categoryId && c.Active && !c.Deleted
                                  && c.Categories.Active && !c.Categories.Deleted
                                 orderby c.SubcategoryName
                                 select new { SubcategoryId = c.SubcategoryId, SubcategoryName = c.SubcategoryName }
            );
            //just added the allow get attribute
            return this.Json(subcategories, JsonRequestBehavior.AllowGet);
        }
        catch { return this.Json(null); }

    }
like image 21
Junior Mayhé Avatar answered Oct 03 '22 15:10

Junior Mayhé


The reason for the DenyGet default is on MSDN with a link to Phil Haack's blog for further details. Looks like a Cross-Site scripting vulnerability.

like image 153
Chris Shaffer Avatar answered Oct 03 '22 16:10

Chris Shaffer


HTTP GET is disabled by default as part of ASP.NET's Cross-Site Request Forgery (CSRF/XSRF) protections. If your web services accept GET requests, then they can be vulnerable to 3rd party sites making requests via <script /> tags and potentially harvesting the response by modifying JavaScript setters.

It is worth noting however that disabling GET requests is not enough to prevent CSRF attacks, nor is it the only way to protect your service against the type of attack outlined above. See Robust Defenses for Cross-Site Request Forgery for a good analysis of the different attack vectors and how to protect against them.

like image 39
Annabelle Avatar answered Oct 03 '22 15:10

Annabelle


I don't know if this is the reason they chose to change that default, but here's my experience:

When some browsers see a GET, they think they can cache the result. Since AJAX is usually used for small requests to get the most up-to-date information from the server, caching these results usually ends up causing unexpected behavior. If you know that a given input will return the same result every time (e.g. "password" cannot be used as a password, no matter when you ask me), then a GET is just fine, and browser caching can actually improve performance in case someone tries validating the same input multiple times. If, on the other hand, you expect a different answer depending on the current state of the server-side data ("myfavoriteusername" may have been available 2 minutes ago, but it's been taken since then), you should use POST to avoid having the browser thinking that the first response is still the correct one.

like image 45
StriplingWarrior Avatar answered Oct 03 '22 16:10

StriplingWarrior