We're building a stateless RESTful API that relies on the content of OAuth2 tokens to identify users and what they have access to. Users authenticate with other means and are given a token, which they then use to authenticate API requests for an hour or so till it expires.
Since we want it to be stateless, we can't use random tokens with a lookup table. The token basically consists of username + start timestamp + end timestamp. (The token is considered valid if it has a valid user and the current time is between the start and end timestamps.) We encrypt the token before sending it to the user and decrypt it each time we get it back in a request.
If we naively encrypt the token with AES in ECB mode, the ciphertexts for a given user's tokens end up very similar to one another, because the usernames encrypt to the same ciphertext every time, and I assume somebody who knew what they were doing could XOR a few tokens together and recover the key fairly easily.
If we encrypt them in CBC mode instead -- with a fixed IV, so we can decrypt them later -- I assume the attack is a little harder (maybe a lot harder? maybe not harder at all?), but the ciphertexts still end up very similar.
What we're currently doing to get around this is reverse the token (bytewise) before encrypting in CBC mode, so the highest-entropy bytes get encrypted first and feed into the rest of the chain, and there's no obvious similarity between ciphertexts, and at least we don't look stupid. But, as they say, anyone can design a cryptosystem they themselves can't break.
- Is this actually doing any good?
- Is there a known best practice we should be following instead?
- If there isn't a known best practice, are there any obvious improvements?
(Note: All of this is happening over SSL so we're not particularly worried about man-in-the-middle attacks etc. But we don't want to encourage any otherwise authorized users to try to forge tokens, either.)