lipp / login-with

Stateless login-with microservice for OAuth

Home Page:https://login-with.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Discuss: Security concerns

lmammino opened this issue · comments

I think it's unsafe to leave sensitive data such as access tokens (in the case of Oauth2 like Google).

If an attacker is able to retrieve a cookie he can easily decode the JWT token and use the access token to issue arbitrary requests to the authentication provider APIs and retrieve any information that might have been originally granted to it by the user (e.g. read my Gmail emails...).

I think the point of this lib is to make this kind of authentication processes stateless (or backendless) and storing the access tokens directly in the cookie is an easy win. Anyway I would at least try to protect this sensitive data by applying some level of encryption, maybe a simple symmetric encryption, using the same secret used to generate the JWT token signature as key would enough...

I look forward to knowing the community thoughts on this matter

Thanks for your feedback! I always like to make this project more secure, so every discussion around this is very welcomed.

These are the security aspects:

  • The cookie is only shared between subdomains (x.foo.com, b.foo.com, www.foo.com, foo.com)
  • The cookie is https only -> no eves dropping / reading traffic
  • The cookie (JWT) is encrypted
  • The cookie (JWT) is http-only (not readable via script attack)

IMHO this is pretty secure. If the attacker still gets the cookie and can decrypt it:

  • why should he/she stop there?
  • how much more security do you get when encrypting the API keys?

I am very interested what other folks have to say!

@lipp, thanks for your work on this project! I have a (potentially naive) question about JWT encryption and thought it might be an appropriate part of this discussion.

My understanding when you say the cookie (JWT) is encrypted is that without a decryption key, it is not possible to read the contents of the cookie. However, I am able to access the contents by Base64 decoding the middle part of the JWT.

Reproduction steps

My JWT (stored in the cookie) according to login-with
screen shot 2017-08-22 at 1 47 50 pm
My payload (access token has been revoked) according to login-with
screen shot 2017-08-22 at 1 47 55 pm

If I copy the whole JWT into jwt.io's debugger, I immediately see my access token available in the payload section:

screen shot 2017-08-22 at 1 53 02 pm

Additionally, if I just copy the middle part of the JWT (the payload), I get the same result.
screen shot 2017-08-22 at 1 53 39 pm

My question

Assuming someone gets my JWT from the cookie, is there anything to stop them from using my access token to access the GitHub API? Is there a misunderstanding on my part between encoding and encryption?

Thanks for your help :)

@danthareja THX for investigating. This might be serious. I'll look into this ...

after thinking about it... this is intentional 😄
The JWT can be read as you described. It is still secure as:

  • Your microservices using that cookie have to validate the signature (LW_JWT_SECRET) and only operate if they are valid.
  • The cookie is bound to your domain (LW_SUBDOMAIN=auth.foo.com -> *.foo.com and foo.com). The browser will never send the cookie to other domains / websites.
  • The cookie is http-only and thus can not be modified by malicious scripts or other attacks.
  • The cookie is "secure" and thus forces you to use https, so nobody can read it (evesdropping)

Hope this makes things clearer.

@lipp - thank you, this does indeed clear things up.

There is a nice strategy to prevent CSRF attacks called Double Submit Cookie. I think it works like this:

login-with adds a random key to the payload of the JWT that it stores in the httponly=true cookie (this one cannot be accessed from the JS client.).

login-with creates another JWT and signs it with the random key from above. This token is stored in another cookie, which is httponly=false (that means it can be access by the JS client).

Now in order to request a secure API endpoint the client JS application has send the token from the httponly=false cookie with the HTTP header.

That means the API receives two tokens for each request, one over the HTTP headers from the JS client and one from the httponly=true cookie.

To validate the user the API decodes the first JWT (the one from the httponly=true cookie) and takes the key from the payload to decode the second JWT (the one from the HTTP header). If both tokens decode the user is valid.

You can find more on this here:

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Double_Submit_Cookie

https://stackoverflow.com/a/37396572/1612318

login-with could provide an env var called CREATE_CSRF_TOKEN=true/false or something. If it's set to flase you just leave the extra cookie and the key in the payload of the httponly=true cookie out.

I think this would be easy to implement (just the first two steps from above). The biggest challenge I see would be the documentation for something like this. ^^