2

I've read that the MVC anti-forgery token defeats CSRF attacks by storing security tokens in a form field and then comparing the default header/cookie value sent automatically by the browser to the field value set by MVC on every request. Since a malicious web site will not know what value to give the field that stores the token, a CSRF attack fails.

So whats the point of storing the token in the header at all? Couldn't you just store the token in a form field on every request manually from Javascript and skip the comparison altogether?

This question applies to me, because I'm creating a single sign on auth scheme for different apps that run on SignalR, ajax requests and ASP.NET forms. I aim to avoid the CSRF attacks by sending the auth token in a custom field for each type of request: hidden field for ASP.NET forms, part of req body for AJAX, and query string for SignalR (with default settings in place to disable cross-origin requests), avoiding default headers and cookies altogether.

  • Where did you read that? Any links? – Marko Vodopija May 12 '17 at 4:42
  • Read this in Apress's "Pro ASP.NET Web API Security" by Badrinarayanan Lakshmiraghavan page 350. – Ian May 16 '17 at 15:45
3

ASP.NET MVC uses a variation of synchronizer token pattern. In a typical implementation, syncrhonizer token pattern works by generating a large random token and preserving it in two locations:

  1. Session state (either on server or client)
  2. In a hidden form field

When a form is submitted, server will check that both values match and fail if they don't. This works because an attacker can't get the token value in advance.

ASP.NET MVC slightly modifies this pattern by not using session state, instead using session cookie. Token also contains some additional data depending on whether it is a cookie token or form field token:

    /* The serialized format of the anti-XSRF token is as follows:
     * Version: 1 byte integer
     * SecurityToken: 16 byte binary blob
     * IsCookieToken: 1 byte Boolean
     * [if IsCookieToken != true]
     *   +- IsClaimsBased: 1 byte Boolean
     *   |  [if IsClaimsBased = true]
     *   |    `- ClaimUid: 32 byte binary blob
     *   |  [if IsClaimsBased = false]
     *   |    `- Username: UTF-8 string with 7-bit integer length prefix
     *   `- AdditionalData: UTF-8 string with 7-bit integer length prefix
     */

Both tokens are encrypted and authenticated and thus not readable on the client side. See the implementation of serializer here. Given the anatomy of the token above you can see that they are not even identical on the client side, both will encrypt to two different ciphertexts and can't be validated there.

You can probably change the behavior in a way that cookie is not needed by storing CSRF token server side in session state. But you really need two of them and you really need to compare them to make CSRF protection work.

EDIT

To answer more directly to your original question

What is the purpose of the default header/cookie in an MVC anti-forgery token?

To give a complete solution that will work all the time and for any application.

It even has an ability to protect login pages:

Anonymous authentication

The anti-XSRF system contains special support for anonymous users, where "anonymous" is defined as a user where the IIdentity.IsAuthenticated property returns false. Scenarios include providing XSRF protection to the login page (before the user is authenticated) and custom authentication schemes where the application uses a mechanism other than IIdentity to identify users. To support these scenarios, recall that the session and field tokens are joined by a security token, which is a 128-bit randomly-generated opaque identifier. This security token is used to track an individual user's session as she navigates the site, so it effectively serves the purpose of an anonymous identifier. An empty string is used in place of the username for the generation and validation routines described above.

As per your comments, you are wondering whether you can do it without cookie by including encrypted username and expiration date into a hidden form field. The answer would be it depends.

Such token could be stolen with an XSS attack whereas an authentication cookie (or CSRF token cookie) marked with httpOnly couldn't. This would make your solution vulnerable in the time frame of your expiration. Whether this is acceptable or not depends on you and the type of the application. For example, a net banking application wouldn't tolerate any kind of CSRF vulnerability but a simple online game could.

You can avoid CSRF altogether if you don't use cookies for storing authentication tokens.

  • Why do you need it in 2 places? If MVC just did authenticated encryption on the token and stored it in a form field, this value would not be knowable by a CSRF attacker, so they would not know how to properly set that form field without the server's secret (presumably some private key). However, I suppose they could generate a random ciphertext and submit these hoping to mess stuff up, so sticking it in the header protects against that, right? – Ian May 12 '17 at 16:57
  • @Ian If it was implemented like you suggested, an attacker could first request a page, get a valid token and then use it in his attack. The token would validate since it was issued by the server. You really need a token tied to a user session, be it on server or client side. But if you dont use cookies for authentication tokens, you don't have to worry about CSRF at all. – Marko Vodopija May 12 '17 at 18:00
  • That should only work if there is no login or user info stored in the token. For instance, if John C Hacker got a token that allowed him to edit his own posts on some forum site, he wouldn't get anywhere sending that token in an attack, because it would only let him edit his own stuff anyways. – Ian May 12 '17 at 18:59
  • @Ian If you take a look here you will see that username is not always contained inside token. But even if it was, what can happen is an attacker stealing such token (with XSS for example) and then use it for CSRF attack. To mitigate this properly, you really need a random number generated for that session and validate it on the server side. You wouldn't need a cookie if you stored this number in server session state or on Redis. ASP.NET MVC just doesn't use server session state. – Marko Vodopija May 13 '17 at 11:46
  • If a hacker steals a token then they can probably steal the session cookie as well and use both to forge requests. The only protection against this is having the tokens expire, but you could put an expiration date into the encrypted form field token. You don't need a cookie to enforce expiration. – Ian May 16 '17 at 16:05
0

It doesn't matter where to put the client side hash cookie or whatever. You can put it even on a hidden field of a form. Everything on client side is going to be accessible anyway. That's not a problem while the legitimate user is who is accessing to them.

The point as you said is to generate it at server side at the same time and then receive it from the client to check if matching is ok. In that way you can legitimate the request. That's all.

Maybe, if an attacker can steal one cookie/hash from a client, he will be able to perform the CSRF attack, but it makes not too much sense... probably if he is able to steal from a client there will be more interesting things to steal like the session cookie for login or something else.

  • Right, so actually putting the token in the standard "Authentication" header cookie and comparing it to the one in the custom field/header is unnecessary. So long as you put it somewhere that isn't automatically submitted by the browser. So I don't get why MVC does it this way. – Ian May 11 '17 at 22:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.