12

I can actually see the verification token key generated by MVC3 framework in plain text when making a request to the server without ssl.

This key is stored in a cookie called: _RequestVerificationToken_Lw__

In mixed security environment it is actually possible to see this token in plain text sent to the server on the initial request to the non ssl site. This token is also static for the duration of the user's session. Then what's the use of having this token when it can easily be stolen by an attacker, because the cookie gets thrown around in plain text.

Shouldn't this cookie be marked as secure and never to be sent across in plain text? Or at the very least be regenerated on every request such that the secure information doesn't leak out of the ssl channel?

I'm talking about this block in MVC 3 AntiForgeryWorker class

private string GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, string salt, string domain, string path)
{
  string forgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(httpContext.Request.ApplicationPath);
  AntiForgeryData token = (AntiForgeryData) null;
  HttpCookie httpCookie = httpContext.Request.Cookies[forgeryTokenName];
  if (httpCookie != null)
  {
    try
    {
      token = this.Serializer.Deserialize(httpCookie.Value);
    }
    catch (HttpAntiForgeryException ex)
    {
    }
  }
  if (token == null)
  {
    token = AntiForgeryData.NewToken();
    string str = this.Serializer.Serialize(token);
    HttpCookie cookie = new HttpCookie(forgeryTokenName, str)
    {
      HttpOnly = true,
      Domain = domain
    };
    if (!string.IsNullOrEmpty(path))
      cookie.Path = path;
    httpContext.Response.Cookies.Set(cookie); //Ma, Why isn't this marked as "SECURE"
  }
  return this.Serializer.Serialize(new AntiForgeryData(token)
  {
    Salt = salt,
    Username = AntiForgeryData.GetUsername(httpContext.User)
  });
}
Alwyn
  • 8,079
  • 12
  • 59
  • 107
  • Could you include a minimal example that reproduces the issue? – George Stocker Mar 19 '14 at 23:51
  • Not sure what example are you looking for, the framework was supposed to dump this token on its own. But just in case, the offending block of code is above. – Alwyn Mar 20 '14 at 00:07
  • The cookie is set to 'HttpOnly', so it isn't insecure. – George Stocker Mar 20 '14 at 00:07
  • Shouldn't it be secured? I mean otherwise a man in the middle can take possession of this cookie and initiate a CSRF attack. Or at the very least the value should be rotated per request, but it isn't. – Alwyn Mar 20 '14 at 00:08
  • 2
    For me it is regenerating new token every request. – Stan Mar 20 '14 at 00:09
  • @Steve as it should be. That's partly why I wanted the OP to show us code; it's easier for us to see what's going on. – George Stocker Mar 20 '14 at 00:09
  • I used firebug, and the value in the _RequestVerificationToken_Lw__ is static. Copy and pasted it on beyond compare and retried and retried and still the same value. I thought this cookie is automatically generated? Is there something else to configure to `rotate`? – Alwyn Mar 20 '14 at 00:11
  • @Alwyn Why it's called _Lw_? Shouldn't it be called *_RequestVerificationToken* by default? Are you trying to implement your own token? Maybe you should stick to built-in and stop worrying. – Stan Mar 20 '14 at 00:12
  • @GeorgeStocker `HttpOnly` does not make a cookie secure, only unreadable from JavaScript. This can only help protect you if your site contains XSS flaws. However, the [Secure flag](https://www.owasp.org/index.php/SecureFlag) can stop the browser from sending it over non HTTPS connections. – SilverlightFox Mar 20 '14 at 09:47
  • fwiw, antiforgery tokens by default have both the secure and httponly flags set. Also, not sure if this changes much, the form token incorporates the user's nameidentifier not found in the cookie token, so as I understand it merely possessing the cookie token won't be enough to validate a forgery. – Matthew Oct 20 '15 at 11:48

3 Answers3

26

That's quite the inflammatory question title you have there.

The built-in MVC anti-forgery functionality is as secure as the application is configured to be. All cookies written to Response.Cookies will be automatically marked with the "secure" modifier if <httpCookies requireSSL="true" /> is set in Web.config (see MSDN docs). MVC's anti-forgery cookie also gets this behavior if this switch is set.

Combine this with other functionality like setting the HSTS header in your responses, and you're essentially providing a guarantee that the browser will never send sensitive data over plaintext channels.

Additionally, the anti-forgery system does allow storing custom data in the tokens, and you can receive a callback to verify the custom data when the token is validated. See AntiForgeryConfig.AdditionalDataProvider for more information.

Levi
  • 32,628
  • 3
  • 87
  • 88
  • That's cool! You can tell I haven't used ASP.NET MVC in awhile. Good on Microsoft for providing those hooks! :) – NathanAldenSr Mar 20 '14 at 13:27
  • 2
    @Levi, isn't that an all or nothing proposition? We have a mixed security site, for performance reason. We do not intend for all cookies to be sent through ssl. Just the ones that we deem "sensitive". This CSRF one fits the bill of "sensitive". Also even with additional data provider, that doesn't get around the fact that the cookie may be transmitted over non secure channel, unless you're thinking I should handle the encryption myself. – Alwyn Mar 20 '14 at 15:26
  • 3
    SSL used to be expensive many years ago. This is no longer the case. Entirely-SSL sites now have the same performance characteristics as mixed-security sites. Nevertheless, you can set the "secure" flag of the anti-xsrf cookie without opting everything in by using the AntiForgeryConfig.RequireSSL property: see http://msdn.microsoft.com/en-us/library/system.web.helpers.antiforgeryconfig.requiressl(v=vs.111).aspx for more info. – Levi Mar 20 '14 at 15:35
  • @Levi I checked AntiForgeryConfig.RequireSSL <-- this property seems to be missing from MVC 3. The System.Web.WebPages, in MVC 3 is version 1, not 2 unlike the link. So yes it looks like in MVC 3 this token is rather vulnerable. – Alwyn Mar 21 '14 at 16:37
  • Then you could hook EndRequest, loop over the response cookies, and set Secure=true for each one you want only sent over SSL. Numerous other ways to accomplish the same thing. Though by far the best solution would be to run the whole site over SSL and flip the master "all cookies should use SSL" switch in Web.config. (If any of your site is not run over SSL, the attacker can still pull tricks that would affect even SSL-only pages.) – Levi Mar 21 '14 at 16:41
  • Cheers for the callout on the inflammatory title. – HumbleBeginnings Feb 04 '19 at 15:13
  • Good answer, but downvoted for accusing another user of being inflammatory. It's a pretty mild title, and, in truth, in a default asp.net setup, even with HTTPS turned on, this cookie will in fact be sent without the secure flag. Remove the accusation and I'll switch it to an upvote, though. – Chris Moschini Dec 21 '19 at 17:30
10

With protection against CSRF attacks, an optimal solution is to always use SSL. Without SSL, yes, the nonce--as it is called--is vulnerable to a MITM attack. When using cookies to store the nonce, the cookie must be marked HTTP-only. This prevents JavaScript from reading the cookie. You should also render the nonce as an <input type="hidden" value="nonce"> tag within all <form>s in addition to a cookie.

Anyone with access to the browser itself would be able to read the nonce, and the only way to prevent a replay attack is to have nonces expire the first time after they are validated for the first time by the server. This approach can cause a terrible user experience when the user uses the back button and resubmits a request with the same nonce, however. Because you're using ASP.NET MVC's built-in anti-CSRF protection mechanism, it may not be easy to change its behavior to only allow a nonce to be used once. (EDIT: Thanks to Levi below for informing me that ASP.NET MVC actually makes this quite simple)

If you want better control over generating and validating the nonces then I suggest rolling your own implementation, as I did with my JuniorRoute framework. In fact, feel free to take a look at JuniorRoute's source code to see how I implemented it. It's too much code for a Stack Overflow post.

NathanAldenSr
  • 7,841
  • 4
  • 40
  • 51
  • 1
    It doesn't really need to be marked as `HttpOnly` - some JavaScript frameworks can use the [double submit cookies](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Double_Submit_Cookies) technique as an easy way to include the cookie value as a hidden form field too (or as a request headers). I would recommend that the `Secure` flag is set though to prevent transmission over plain HTTP. – SilverlightFox Mar 20 '14 at 09:44
0

My Take

a) The form submission is deemed not forged based on comparison of

  • __RequestVerificationToken cookie &

  • __RequestVerificationToken form field.

The 2 values are some kind of symmetrically match and hence not same.

b) Cookie can never be marked default must-use-secure-channel by the framework because some applications do not use https.

c) The __RequestVerificationToken implementation is protection against CSRF & cannot help valid user from snooping into process memory:p.

Pushpendra
  • 820
  • 7
  • 11