lynndylanhurley / devise_token_auth

Token based authentication for Rails JSON APIs. Designed to work with jToker and ng-token-auth.
Do What The F*ck You Want To Public License
3.54k stars 1.13k forks source link

Where to store token securely? #1281

Open Uysim opened 5 years ago

Uysim commented 5 years ago

I have been review this https://github.com/lynndylanhurley/devise_token_auth/issues/1005 the answer in this issue is not correct. We need to guide the way to handle token securely on client side. Nothing I know from this gem to store token securely.

I think the better way is to handle token with HttpOnly cookie

MaicolBen commented 5 years ago

I don't have experience with HttpOnly cookie, but can you submit a PR changing the docs suggesting the best security recommendation according to you?

CDimonaco commented 5 years ago

This gem provide the secure token, how you want store it, it's a completely different stuff, in a single page application context is correct to store the token on the local/session storage, according to your needs, you can also put in the cookie, but it's just the same level of security, a cookie can be also stolen on the client side. If you want more security you can just encrypt your local storage data, but it's a false sense of security, because you will need the key for decrypt the stuff, and it's also on the client side.

If you want stuck with the token in a cookie, I suggest you to use the standard Devise gem. using a cookie for the authentication facilities that this gem offers, does not makes sense, just imagine the process of change the authentication token and other 'headers' stuff, of this gem, and at each request recreating the cookie, it's just an overkill and overhead.

Uysim commented 5 years ago

@MaicolBen I also never experience HttpOnly cookie as I use this gem for years.

But recently I research deep on this on the internet, I can see HttpOnly cookie is the cookie that can only read on the server. So no one on client side can access it. Every request browser will attach this kind of cookie to the server automatically.

I try to read docs and review the source. Seem like it no way to have this kind of security in this gem yet. Also, currently I try to implement it by myself. But it need ton of customization.

That why I come to ask the suggestion from community. If anyone use to take care of this security by simplify?

Uysim commented 5 years ago

@CDimonaco Actually, it is not completely different staff. It is the same the way you provide a secure token and there is no way to store that secure token by the way you provide.

If you work with Mobile application, it is ok to store that kind of token on the client side. But if you working with single page application it is the scary part.

  1. Do you feel safe it under local storage? Some can get by just run localStorage.getItem('access_token') on inspect element.

  2. Session storage maybe more secure but you client will need to login with username and password all the time after they closed their browser

  3. Devise gem is good, but I think it far beyond the one page application.

  4. Would you scarify the security performance over the performance? We should won't our user data at risk because of the performance.

I think we should concern the way provide the secure token in order be more secure. What is your idea? Let's me know

lynndylanhurley commented 5 years ago

@Uysim I'm also interested in knowing the best way to handle this.

All of the clients I've built for this store the tokens in localStorage or as cookies and then append the tokens as headers to the API with each request. This makes the sessions really easy to manage on the client-side, to the point where we can easily have multiple concurrent sessions running at the same time if we want to.

The issue with localStorage is that if a malicious script makes it to the client codebase (i.e. compromised NPM package, browser extension, etc.), it will be able to read the contents of localStorage and use the credentials to gain access to the user's account.

HttpOnly cookies avoid this problem by not allowing scripts to see the contents of the cookies. But this does not solve the issue with malicious browser extensions as they can actually see the contents of HttpOnly cookies.

So if I understand the issue correctly, HttpOnly cookies really just avoid the threat of compromised code running in our own codebases. I'm not saying that this isn't a serious threat, but from my experience there's a pretty low risk of this actually happening.

The problem with HttpOnly cookies is that it makes the credentials more difficult to manage on the client, and the trade-off hasn't been worth it in my opinion.

I would like it if someone could provide a detailed explanation of how this kind of exploit would happen. That would make it easier for us to gauge the severity of this problem.

I am interested in finding a way to use HttpOnly cookies with this lib. I'll leave this open and keep investigating.

theblang commented 3 years ago

@lynndylanhurley An interesting side note about using an HTTP cookie over localStorage is that you can share the auth state in the client across subdomains (ex. domain.com, app.domain.com, blog.domain.com, admin.domain.com). With localStorage a user would have to re-authenticate when changing between the subdomains (because the subdomains can't access each other's localStorage), but with a cookie you can use the Domain attribute to share the user's auth state. The security aspect is a nice icing on the cake, but really it's this sharing capability that is driving us to transition from localStorage to an HTTP cookie.

See this bit from the MDN docs on cookies:

Domain attribute

The Domain attribute specifies which hosts are allowed to receive the cookie. If unspecified, it defaults to the same origin that set the cookie, excluding subdomains. If Domain is specified, then subdomains are always included. Therefore, specifying Domain is less restrictive than omitting it. However, it can be helpful when subdomains need to share information about a user.

For example, if Domain=mozilla.org is set, then cookies are available on subdomains like developer.mozilla.org.

booleanbetrayal commented 3 years ago

Thinking this could work behind a config switch without a whole lot of hassle.

theblang commented 3 years ago

Here is a PR I'm working on to support using HttpOnly cookies as the transfer and storage mechanism for the Bearer token. Note that enabling cookies using the new cookie_config property will only be additive, not a switch (yet). The reason for that is because ng-token-auth would require changes as well to support using only an HttpOnly cookie. Ultimately, as @Uysim mentioned, using only an HttpOnly cookie would provide protection against XSS attacks. But for now, at least supporting cookies in addition to headers will allow us to share the auth state across subdomains (using the Domain attribute).

theblang commented 3 years ago

Now that the the aforementioned PR has merged, we should also make the necessary changes to allow only a domain cookie to be used. By doing that, we'll accomplish the security aspect of using an HttpOnly domain cookie (currently we're still exposing the token in headers, so you only get the subdomain sharing). One thing to note is that we'll need to make it dynamic, not a config. This is to handle a use case such as a server that is serving both web clients and Cordova clients (which do not support cookies). I'm hoping to begin work on this at some point in the near future.