c# mvc? - ASP.Net MVC CSRF Prevention for JSON POST





what webforms (2)


This question brings up an interesting discussion.

Provided that the request Content-Type is application/json, then CSRF is not a concern. This is because application/json requests must be submitted via XmlHttpRequest, and the cookie which is a necessary part of the verification of your AntiForgeryToken cannot be passed cross-site, but must adhere to the Same Origin Policy.

However, it is possible for a malicious user to submit a request via application/x-www-form-urlencoded which contains the information which will appear to be a valid JSON request, and which will pass any authorization cookies back to your application. There is a more detailed discussion of this at http://forums.asp.net/t/1624454.aspx/1?MVC3+JSON+Model+binding+not+working+with+AntiForgery and at http://aspnet.codeplex.com/workitem/7472, where I post a proof-of-concept.

While it is possible to include the __RequestVerificationToken in a JSON request, a better line of defense is to create an Attribute to verify that a request is of type application/json, since any other request being submitted to your action which expects JSON is in fact invalid, and should not be handled.

I expect that this security issue will be addressed in MVC 4.

UPDATE:

Here is a simple AuthorizeAttribute class you can use to decorate any actions which expect to receive JSON:

public class JsonRequestAttribute : AuthorizeAttribute
{

    /*
     * 
     *   CONFIRM that this is REALLY a JSON request.
     *   This will mitigate the risk of a CSRF attack
     *   which masquerades an "application/x-www-form-urlencoded" request
     *   as a JSON request
     * 
     */

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
         if (!filterContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
         {
             // This request is masquerading as a JSON request, kill it.
             JsonResult unauthorizedResult = new JsonResult();
             unauthorizedResult.Data = "Invalid request";
             unauthorizedResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
             filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;
             filterContext.Result = unauthorizedResult;
         }
    }
}

I'd like to close the CSRF vulnerability for posting raw JSON via AJAX.

I'm familiar with MVC's mechanism for automating CSRF prevention using the ValidateAntiForgeryTokenAttribute and @Html.AntiForgeryToken(); however, if I understand correctly, this mechanism requires that the POST be done with a Content-Type of application/x-www-form-urlencoded (or similar). Is there a built-in mechanism in ASP.Net MVC that will reject CSRFs for POST requests with Content-Type of application/json? If not, am I stuck with putting the anti-forgery into the JSON object itself? Can you recommend a technique for protecting JSON POST requests from CSRF vulnerability with the same level of security as the form-based approach built into ASP.Net MVC?




In short, what you've just described is not an example of an XSRF attack...


What is an XSRF attack?

Both CSRF and XSRF are used to describe what's called a Cross Site Request Forgery. It's where a malicious website takes advantage of your authenticated state on another website, to perform fraudulent cross-site requests.

Example: Online banking.

The Bank

Imagine that you're authenticated on your bank's website, and that your banks website contains a form to create new transactions, all pretty straight forward...

<!-- Your bank -->
<form action="/send_moneh" method="POST">
    <input type="text" name="amount" />
    <input type="text" name="accountNumber" />
    <input type="submit" value="Send Money" />
</form>
The Malicious website

Now let's think of the Malicious website you're also visiting, imagine that it also contains a form, one that is hidden and the values of which are pre-populated...

<!-- Malicious website -->
<form action="http://yourbank.com/send_moneh" method="POST">
    <input type="hidden" name="amount" value="100.00"/>
    <input type="hidden" name="accountNumber" value="123456" />
</form>

When the form on the malicious website is submitted, an HTTP request will be sent straight from you to your bank, and because you're authenticated on your bank's website, the transaction could be accepted.

Essentially, an attacker is using your own authentication against you by forging requests and using you as the messenger to deliver that request.


How do prevent it?

You use an anti-forgery token, this token is a string containing a random value, the token is placed in your cookies, in addition to your HTML forms.

When you receive a request, you validate that the form contains an anti-forgery token and that it matches the one stored in your cookies. A malicious site can not see the tokens your website sets on a client, and without this information, XSRF attacks are stopped in their tracks.


How do I implement it in ASP.NET MVC?

On your controller Action that will be handling the request, add the attribute [ValidateAntiForgeryToken], and in the HTML form add (@Html.AntiForgeryToken()).

public class ExampleController : Controller
{
    [ValidateAntiForgeryToken]
    [HttpPost]
    public ActionResult Test(Foo fooModel)
    { 
        // do your thing...
        return this.View();
    }
}


<form action="/Example/test" method="POST">
    @Html.AntiForgeryToken()
    <input type="text" name="bar" />
    <input type="submit" value="Submit" />
</form>

That's it!


Tips/Pointers/Advice

Anti-Forgery Tokens don't make a lot of sense when performing GET requests, in fact, they don't make sense to have them anywhere that you're not modifying and persisting data, as any GET request will be returned to your user, not the attacker.

If you're Creating, Updating or Deleting data... make sure that you're using it then.





c# asp.net-mvc asp.net-mvc-3 csrf