AJAX POST for Web API Controller and CSRF

I basically need to protect against Cross-Site Request Forgery in my web API controller, which is part of the MVC application. I am open to any ideas. At the moment, I have an MVC View that displays an Esri map using ArcGIS for the JavaScript API. The user creates a route on the map, and the route and various functions that it crosses can be saved using AJAX POST. The view has no form. This is due to the fact that all the data that I POST on the server is stored in memory and does not appear on the screen (or in hidden fields).

So, I have the following in my MVC View in order to get tokens against falsification:

@functions{ public string GetTokenHeaderValue() { string cookieToken, formToken; AntiForgery.GetTokens(null, out cookieToken, out formToken); return cookieToken + ":" + formToken; } } 

I had this hidden input in the view, but he realized that it was bad, because it had both a “form token” and a cookie marker used with AntiForgery.Validation:

 <input type="hidden" id="antiforgeryToken" value="@GetTokenHeaderValue()" /> 

Then, in a separate JavaScript file (not in the script tag in the view), I do an Http POST for my web API controller. Here I add tokens to the request headers:

 var headers = {}; headers['RequestVerificationToken'] = $("#antiforgeryToken").val(); // Ajax POST to Web API Controller $.ajax({ async: true, url: '/api/RouteData', type: 'POST', headers: headers, data: requestData, dataType: 'json', error: function (xhr, statusText, errorThrown) { console.log('Error saving route data! ' + errorThrown); }, success: function (result) { } }); 

NOTE: the data that POSTed in the body is in memory is inside the Dojo user widget (since the page displays an Esri map using ArcGIS for JavaScript). There is no form on the page because the user is not entering data.)

Binding everything on the server side in the web API controller:

 [System.Web.Http.HttpPost] [ResponseType(typeof(RouteData))] public async Task<IHttpActionResult> PostRouteData(RouteDataViewModel routeDataVM) { try { HttpRequestMessage httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage; ValidateRequestHeader(httpRequestMessage); } catch (Exception ex) { _logger.Log(LogLevel.Error, ex, ex.Message); throw; } // Now that we know user is who they say they are, perform update } void ValidateRequestHeader(HttpRequestMessage request) { string cookieToken = ""; string formToken = ""; IEnumerable<string> tokenHeaders; if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders)) { string[] tokens = tokenHeaders.First().Split(':'); if (tokens.Length == 2) { cookieToken = tokens[0].Trim(); formToken = tokens[1].Trim(); } } AntiForgery.Validate(cookieToken, formToken); } 

AntiForgery.Validate is what checks tokens.

I see this SO post ( http://stackoverflow.com/questions/4074199/jquery-ajax-calls-and-the-html- antiforgerytoken / 12116344 # 12116344 for more possible solutions ), which gave me some ideas, but not quite solved the problem for me. Thanks a lot for posting on asp.net: https://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks .

What makes me different for me (I think) is that my JavaScript is in a separate file and cannot call the Razor function on the server side to get illegal tokens, right? Any ideas on how to protect CSRF without form?

+5
source share
2 answers

CSRF protection is what you should do to do just that, protect against cross-site request forgery.

A quick overview of CSRF :

Cross-site request forgery (CSRF) is an attack that forces the end user to perform unwanted actions in the web application in which they are currently authenticated. CSRF attacks are specifically designed for stateful requests, not data theft, because the attacker is not able to see the response to a fake request. With a little help in social engineering (for example, when sending a link by email or chat), an attacker can trick users of a web application into taking actions to select an attacker.

The following is an example of a CSRF attack against your web API if it is not protected:

 <form action="http://yourapi.com/api/DeleteAccount"> <input type="text" name="id" /> <input type="submit" /> </form> 

By submitting this form from a hacked website, if you are logged into your web API using cookie authentication (as an example) as an administrator account, it can delete accounts from your system.

This is because when you are redirected to your API, your browser goes through a cookie, which is stored in the yourapi.com domain, thus authenticating the user and performing the required actions. This attack vector is different from using, for example, a token carrier to authenticate with your API, because the third party does not know this token.

As you correctly stated and implemented, the use of an anti-fake token can protect against this as a generated token sent to the client in a web response and then sent back by the client and confirmed to ensure that this is a permitted request from somewhere we expect. As a token-holder, a third-party organization may not know the token that you sent in your request from the server. In this regard, what you implemented is absolutely true, and passing the token to the client is how we expect it to work.

In ASP.NET MVC (sans API), this is implemented as follows:

 <form .. /> @Html.AntiForgeryToken() </form> 

.. and verified server side with:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Action(MyModel model) { // .. } 

When you call @Html.AntiForgeryToken() , this method sets a cookie (containing a cookie token) and sends it to the client, and also generates a <input type="hidden" ..> to send to the client. Then they are sent back and checked on the server side.

Armed with this knowledge, you can see that your concern about sending a “cookie token” and a “form token” is unfounded, as this is what happens anyway.

I see your cause for concern, but what you are trying to mitigate seems to be a Man in the Middle (MitM) attack, not a CSRF. To get around a significant portion of MitM attacks, you need to make sure your site / API works through HTTPS. If your client is still vulnerable to a MitM attack, your API is likely to be of the least value to the attacker.

+2
source

After some discussion with others, it seems that the solution I implemented is actually in order. Yes, both tokens are in hidden input fields. However, by the very nature of what CSRF is trying to protect from - another site trying to POST on your behalf, this solution works fine. If I am on my site, authenticated, and switch to another site that is trying to POST on my behalf, the site will not have the necessary tokens.

+1
source

Source: https://habr.com/ru/post/1263483/


All Articles