Our company has decided to ditch server side sessions in favor of storing authenticated information such as user id and access privilege information in a cookie.

Before everyone jumps up and down about this approach, given that this is a business decision - how would I do this right? The basic need is to prevent user from tampering the cookie to avoid promoted privileges / or pretend to be a different user and also hides the information. Assumption is all systems are running HTTPS hence let's pretend we won't have cookies being hijacked and replayed etc.

I've read Should we MAC-then-encrypt or encrypt-then-MAC? but I just want to know if this naive approach can work: (plaintext below is my cookie in json format).

// where only 1 secret for AES is used
aes-ctr((plaintext || sha2-256(plaintext)) || iv 

Or do I have to do this?

// where 2 secrets are used, one with HMAC and one with AES
let E be: (aes-ctr(plantext) || iv)
then E || hmac (E)

Or are both approaches insufficient?

There are a few key difference between the 2 approaches:

1st approach: In the digest part, we are doing the simple sha256 without the use of nonce or key.

2nd approach: We are using HMAC with one key and another key for AES.

Based on my limited understanding, the listed arguments against a plain digest as oppose to HMAC is due to opportunities for length-extension based attacks. However in my case, given I've AESed the digest, there should be no such opportunity.

Other attacks listed in here: http://www.thoughtcrime.org/blog/the-cryptographic-doom-principle/ appears to be applicable for block mode only, where we are using CTR.

Apologies if this question appears naive - I am a newbie in cryptography.


1 Answer 1


I am going to answer my own question based on the comment from Ricky Demer: https://crypto.stackexchange.com/a/16431/991

Given the attacker knew what the plaintext should be, for example:

{"userId":1}  --> this is knownText

And with the first approach, which was

aes-ctr((plaintext || sha2-256(plaintext)) || iv 

Attacker can simply perform

(knownText || sha2-256(knownText)) XOR cipherText

To compute the AES cipher stream. Without having to know the key, the attacker can then compute:

(modifiedText || sha2-256(modifiedText)) XOR [deduced AES cipher stream]

Where modifiedText can be

{"userId":2}  --> I want to pretend to be user 2

and then append the original IV.

This would decrypt fine and also validates fine given the first approach. Hence it is insecure.


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.