0
$\begingroup$

I designed a simple library (golang) for use as session token for web applications. The idea is simple:

  • the token will be 16-byte long, denote it as t
  • generate a random byte m, put it into t[0]
  • encode the expiration timestamp as a big endian uint64, append the lower 48-bit of it into t.
  • append the big endian representation of user-id into t
  • append m to t (i.e. the token's first and last byte is same)
  • encrypt the token with AES/CBC, using a random key, and all-0 IV.
  • hex-encode the encrypted token, output the resulting 32-byte string as the final token

In my view, this scheme has the following pros and cons:

PROS:

  • does not use a token store, hence is very scalable, if the web application has a lot of concurrently online users.
  • verification of the token is very fast, and does not use network operation, such as querying a database or cache service like redis.

CONS:

Revokation of individual token is difficult. Currently, I use a "blacklist" to record revoked tokens, which of course is not in-line with my initial purpose of a system that does not consume memory. However, it is easy to revoke all tokens by simply generate a new random key.

My question here is: for my purpose is the system considered SECURE? In general, why most session tokens are stored in DB or cache service, and cryptographic tokens are rarely used (are they)?

Reply to comments:

  1. About the encryption key

In my scheme, the encryption key is randomly generated, and kept in memory, and is NOT SHARED to anyone. The login API use this secrete key to generate the token, and the validation function of other APIs use this key to check if the token is valid or not. The client or any other party do NOT know this key. If this key is re-generated (e.g. server program restart), all issued key is instantly unusable.

Also, chosen plain text attack is just like brute-force guess of the encryption key. If I need higher level of security, I may use AES/GCM or other way to prevent it, e.g. limit the rate of token validation by client IP address.

  1. About replay prevention

I don't know why we need "replay" prevention in access tokens. Common scenario is:

  1. use login api to obtain a token by authenticate use username/password (or otp, sms code etc). This is usually protected by HTTPS.
  2. In the life time of the access token, the client can use it to access any other APIs. The only thing we can probably increase the security is bind the token with the IP address of the client, however, in modern mobile networks, change of client IP is inevitable.

In summary, I don't really care about people sharing their access token. It is analogous to you give the door key of hour house to your friend. The only thing a system should prevent is thief, which is prevented by HTTPS, and illegal duplicate of your "key", which is prevented by proven AES algorithm.

  1. Other considerations/questions
  • big endian vs. little endian: I just choose it arbitrarily. I will use little endian to improve performance. Thanks @SAI Peregrinus
  • Why we need authentication in design of an access token? In theory, how can we prevent share of an access token, except by bind the token with visitor IP address?
  • JWT or PASETO allow you to encode data directly into the token. But my naive token only encode an ID. Whether you should carry your data directly in the token, or just use your token as a key to query the server for your data, is a bigger design consideration, outside the scope of this post.

UPDATE

I updated the token library, now it uses standard AES/GCM with random IV.

$\endgroup$
5
  • $\begingroup$ FYI paseto.io already exists, has Go implementations, is fast, and is secure. $\endgroup$ Commented Jul 27, 2022 at 12:53
  • $\begingroup$ Why big-endian? Almost every system uses little-endian, this adds work but no security. How is the AES key shared? Why CBC? $\endgroup$ Commented Jul 27, 2022 at 13:07
  • $\begingroup$ Do you need replay prevention? You don't have any. It may not be needed, but without a token store you can't prevent an attacker from re-sending a token. You're also lacking any authentication, so it's trivially vulnerable to chosen ciphertext attacks. $\endgroup$ Commented Jul 27, 2022 at 13:09
  • $\begingroup$ @SAIPeregrinus There is no CBC here, only AES / ECB (without padding). An all zero IV, a single block and no padding :) $\endgroup$
    – Maarten Bodewes
    Commented Jul 27, 2022 at 13:44
  • $\begingroup$ "encrypt the token with AES/CBC, using a random key, and all-0 IV. " Turns CBC into ECB. Why bother? $\endgroup$ Commented Jul 29, 2022 at 12:47

1 Answer 1

2
$\begingroup$

This isn't a good idea. As there isn't authentication tag, and assuming there are many valid user ID's and expiration time stamp, one out of 256 generated tokens will have an identical first and last byte $m$.

I don't see big problems with replay, assuming that you aren't afraid that users will swap tokens. As there is an explicit timestamp users should not be able to reuse it at a later date (assuming that the time-stamp uses UTC and that you don't use micro-seconds or worse, as that would break after 9 years - assuming you start the counter at this date - seconds from 1970 would work fine).

Encryption is commonly used to achieve confidentiality; it doesn't magically authenticate entities or their connections. Please use an existing framework.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.