🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Auth tokens

Started by
6 comments, last by hplus0603 5 years, 7 months ago

I want a token authentication system, you know, so users don't have to bother filling out username and password and can immediately start to have fun. I used to just store a random string both locally and in the database and use that together with the username to create a automated login system but I guess that is relatively easy to abuse. Now I have started with JWT but I have no clue what I am doing. I am running a basic client/server architecture on java using KryoNet.

The following code creates and returns a token as a String:


public String generateToken(String subject) {
   Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
   return Jwts.builder().setSubject(subject).signWith(key).compact();
}

First thing I am wondering is if this is "secure enough"? I really don't need more security then any other proper game out there. I guess a username (as the subject) is easy to guess but does the key provide enough security or do I need to add additional claims?

Now I guess I have to store that key in the database alongside the user credentials and send the token to the client to be stored locally for reuse. Then I can do the following when I need to authenticate the user.

  • Send the token and username from the client.
  • Get the key from the database using the username to lookup.
  • Parse the token using the key and username like so:
    
    Jwts.parser().setSigningKey(key).parseClaimsJws(token).getBody().getSubject().equals(subject);

Other then login the user how often should I check the authentication token? Would just the login be enough or perhaps just each "sensitive" request from the client? Like I said I am using Kryonet once a connection is established I use that connection to handle the actions for that particular client, this makes me think that just the initial login is enough. Someone connecting from somewhere else would get a different connection and needs to have a proper token or login credential to do anything user related.

 

Advertisement

The best way to do token security is to generate a fully random value, rather than trying to generate a value using hashing. Store the username and the random value locally, and on the client. (If you're in a browser, localStorage might be a good place to store this.)

This random token will typically work as "session token" and could be the value of a cookie and such, too.

16 bytes of random data should be plenty. Just make sure it's a quality random source, such as /dev/urandom on Linux, or the crypto random provider on Windows. (Java likely has some API for accessing these; I don't know off-hand what it is.)

The next question is, once this token is lost, how does the same user use the same username again? Is the username lost forever? Do you also require an email address, so you can mail a new token or token-factory link to the user?

enum Bool { True, False, FileNotFound };

A random string, that is actually how I did it before and used a org.bson.types.ObjectId. Having the username and token let's anyone login counts for both options but I do think that using a security library will add an additional layer. Apart from that, a token can hold additional data such as privileges, I'm not sure if I will use it but by implementing JWT with just a couple lines I got that functionality at hand. Is there any particular reason why you value generating a random key over hashing on the server side?

Sure the user can register by a traditional email/password or link it with social media platforms and is notified every now and then if he wants to register or link his account. If the user does neither and the token is lost, most likely by removing the app the account is lost.

 

3 hours ago, menyo said:

Sure the user can register by a traditional email/password or link it with social media platforms and is notified every now and then if he wants to register or link his account. If the user does neither and the token is lost, most likely by removing the app the account is lost.

If that is your intent, you can treat this like a regular website "remember me" feature. Generate accounts with no initial email/password (making them impossible to login normally or recover!), which can be filled in later like a normal account update / password change. You might also want to delete such accounts periodically if there has been no recent login. You also want to be sure search bots and the likes don't keep getting new accounts generated for them.

 

Your server side web platform likely provides most of the required components directly, and has well tested libraries (which would also likely give you things like Facebook and Google login).

 

"Remember me" tokens are generally a random value generated by the server and stored in the database in a hashed form (so that in the event of a read-only DB leak, the attacker still cant login), while an original copy is provided to the client. But an alternative is just to have a long lived regular login session token/cookie. The email/sms/etc. verification and password reset features use similar hashed tokens as well, but instead of storing in the browser it is emailed, texted, etc. to you. In the case of short tokens for manual entry (e.g. SMS) be very careful about brute force attacks. All are likely readily available in tested forms for you with suitable protections.

 

Cookies are common for websites as the browser sends these automatically so they work on regular page/document loads, images, etc. Local storage or such would generally be more effort for not much gain, and does not work to authenticate a regular browser resource load as you need JavaScript to deal with it. But be aware of Cross Site Request Forgery (CSRF), most frameworks will have protections built in, but be sure you don't do anything to go around them.

Passwords should absolutely be suitably hashed by a secure algorithm (and no, even a secure hash like SHA256 or such by itself is not enough) when stored on the server, since as well as protecting yourself from a data leak, people tend to reuse passwords and they tend to be words.

 

As for session logins themselves, there are several options, so check what you are using. Some may just store an ID in a cookie (then the server must look this up every request against a centralized store), while others store the actual data in a protected form (signed or encrypted. This gives the server everything it needs and doesn't need a central session store).

 

JWT itself is fairly complex and has some pitfalls, if your framework already provides something, or has well known libraries for user authentication, I would recommend that rather than your own JWT.

16 hours ago, menyo said:
  • Send the token and username from the client.
  • Get the key from the database using the username to lookup.
  • Parse the token using the key and username like so:

For a start this is not really the right concept for JWT, generally the token would contain the username/id, etc. itself. In fact one of the points of JWT is that server A can authenticate the user and give the user the token, then the user sends the token to server B. Server B uses that to know who the user is without asking A. This means all the tokens generated by A are using the same key so that B is able to validate them. If you only have one server, or you intend for B to ask A, then what is this adding? It could have just been a large random value.

This is actually based off a lot of existing technology but standardised to be usable between different platforms which may be using different languages, written at different times, by different people, and to cover the situation where server B trusts A, but A does not trust B which all sounds great and secure. But still, there are a lot of security pitfalls with it so be very careful. For example B accepting very old tokens that were maybe lost, or allowing insecure algorithms allowing for token forgery. Perhaps the worst case being servers that accepted tokens with the "none" algorithm as valid, or if using a public key, allowing the algorithm to be changed to a symmetric one (using that public key as if a secret key...).

There are two kinds of tokens:

1) Purely random IDs, used to look up everything (user name, privileges, etc) in a server-side database. The only important thing here is that the token is hard to guess (strong randomness of enough bits!) These tokens are generally quite secure, can be revoked on an individual token basis, but require a look-up when presented. This is the "web session cookie" kind of token.

2) Information bearing tokens, containing user name, privileges, and other information. These tokens need to be time limited (so you can't steal one and use it forever,) and need to be signed using a key shared between token issuer, and token user (if you have a login server separate from game server, for example.) These tokens have the benefit that no database look-up is needed, but they are harder to implement, and can't be individually revoked -- you basically have to change the signing key, revoking all tokens, if you believe some token has been compromised. This is the "kerberos ticket" kind of token.

I strongly recommend token kind 1) unless you have a very specific reason to need kind 2).

ObjectId is very bad for kind 1) tokens. They are almost 100% deterministic, and very easy to guess given a few examples of valid tokens and knowing when the token in question was supposed to have been generated.

enum Bool { True, False, FileNotFound };
On 8/16/2018 at 1:12 PM, SyncViews said:

JWT itself is fairly complex and has some pitfalls, if your framework already provides something, or has well known libraries for user authentication, I would recommend that rather than your own JWT.

If I get this right, with JWT, the need of a secure connection over https isn't really necessary, because the sent data is already strongly encrypted and gets decrypted on each side?

The data in the token is potentially encrypted and signed, but that doesn't protect it against interception and re-use in transit, unless you also have transport security (such as HTTPS.)

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement