Home > other >  Is storing access token in cookie to allow for SSR dangerous?
Is storing access token in cookie to allow for SSR dangerous?

Time:12-16

I'm working on a project where I've got a central API server and then multiple microservices for it including a website. The website uses OpenID to handle authentication. To allow for server-side rendering on the website yet have it remain stateless, I'm storing the access token in a cookie which is being used on the server each time the user requests a page. Is there an exploits that could happen from this? As far as I'm aware I shouldn't have any problems with CSRF or any other exploit like it, however I haven't seen this way of handling authentication before.

CodePudding user response:

Short answer: Yes

Long answer

The definition of CSRF is that the authentication cookie is automatically attached when any request from anywhere to your website is made. You will always need to implement xsrf counter measures frontend.

On each webrequest the webbrowser makes to the server, the server attaches a non-httponly cookie to the response, containing a CSRF-token which identifies the user currently signed on (NuGet).

public async Task Invoke(HttpContext httpContext)
{
    httpContext.Response.OnStarting((state) =>
    {
        var context = (HttpContext)state;
        //if (string.Equals(httpContext.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase))
        //{
            var tokens = antiforgery.GetAndStoreTokens(httpContext);
            httpContext.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { Path = "/", HttpOnly = false });
        //}
        return Task.CompletedTask;
    }, httpContext);

    await next(httpContext);
}

Your frontend must be configured to read this cookie (this is why it's a non-httponly cookie) and pass the csrf-token in the X-XSRF-TOKEN header on each request:

HttpClientXsrfModule.withOptions({
  cookieName: 'XSRF-TOKEN',
  headerName: 'X-XSRF-TOKEN'
}),

Then you need to add and configure the Antiforgery services to the ASP.NET Core application:

services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

Now you can decorate your controller methods with the ValidateAntiforgeryAttribute.

I'm using angular, and angular does not send a X-XSRF-TOKEN header when the URL starts with https:. This could perhaps also be the case for React, if they provide an embedded solution.

Now if you combine this with the cookie authentication provided by ASP.NET Core Identity (SignInManager.SignInAsync), you should be clear to go.

Important links

  • ASP.NET Core docs
  • For react I cannot find documentation on CSRF, but the idea is explained in the answer

More info

A hacker could try and send you an email with a link to a Facebook URL. You click this link, the webbrowser opens up, the authentication cookie for facebook.com is automatically attached. If this GET-request consequently deletes posts from your timeline, then the hacker made you do something without you realizing.

Rule of thumb: Never change state (database, login, session, ...) on a GET-request.

A second way a hacker could try and trick you is by hosting a website with the following html:

<form action="https://facebook.com/posts" method="POST">
    <input type="hidden" name="title" value="This account was hacked">
    <input type="hidden" name="content" value="Hi, I'm a hacker">
    <input type="submit" value="Click here and earn 5000 dollars">
</form>

You only see some button on a random website with an appealing message, you decide to click it, but instead of receiving 5000 dollars, you're actually placing some posts on your facebook timeline.

As you can see, this is totally unrelated with whether you're hosting a single-page or MVC application.

Defense

MVC applications

In MVC websites, the usual practise is to add an input containing a CSRF token. When visiting the page, ASP.NET Core generates a CSRF token which represents your session (so if you're signed in, that's you). When submitting the form, the CSRF token in the POST body must contain the same identity as the one in the Cookie.

A hacker cannot generate this token from his website, his server since he isn't signed in with your identity.

(However, I think that a hacker would be perfectly capable of sending an AJAX GET request from his website with you visiting, then try to extract the token returned from your website and append it to the form). This could then again be prevented by excluding the GET-requests which return a form containing a CSRF-token from CORS (so basically don't have a Access-Control-Allow-Origin: * on any url returning some CSRF-token))

Single-page applications

This is explained on top. In each webrequest made to the server, the server attaches a cookie to the response containing the CSRF-token for the current user session.

The SPA is configured to read this XSRF-TOKEN cookie and send the token as X-XSRF-TOKEN header. The cookie can only be read by scripts from the same website. So other websites cannot host a form containing this token field for someone's identity.

Although the XSRF-TOKEN cookie is also sent along to the server, the server doesn't process it. The cookie value is not being read by ASP.NET Core for anything. So when the header containing a correct token is present on the request, the backend can be sure that the webrequest was sent by your react (or in my case angular) app.

CodePudding user response:

So when you say you are storing your access token in a cookie, you must be attaching the access token to the Authorization header of each request.

What grant type are you using?, I hope you are not using implicit flow. Here's a link and here's short one I wrote

You'll also want to configure CORS on your server and allow requests only from certain origins.

Can you tell us a bit more about your technology stack?

  • Related