I make session cookies of form session_id+'|'+hmac_sha256(session_id, static_secret) where session_id = random_string(16 bytes) made with a PRNG (not a CSPRNG!) seeded on the server's timestamp at start-up.

The cookies have the Secure flag so transport security is left to TLS to solve.

The session id is authenticated statelessly (i.e. without the need for a session database) the same way it was created, i.e. with hmac_sha256(session_id, static_secret).

Is this breakable? If so, how?

Notes from brynk's answer:

  • A revocation list would have to be queried as a second authentication step to allow for killing sessions on stolen devices. This will make the session authentication stateful (though the list would likely be short).

  • The secret has to have 256bit+ of entropy if using HMAC-SHA256. Your cat should be able to handle that in 2-3 walks over the keyboard.

  • Using a PRNG for session id as opposed to say, an auto-incremented id does not make the cookie more secure. The reason I prefer it is because 1) it's stateless (don't have to keep a next_id file around), and 2) it hides the information of knowing how many sessions were generated before yours.

  • maybe you can find a way of determing server uptime?
    – brynk
    Commented May 21, 2021 at 20:13
  • Why not use a library/framework that implements this? Commented May 22, 2021 at 3:53
  • @brynk Thanks for the comment. I don't know how you could do that, but even if say you could find a way to determine the server uptime down to the millisecond, at best you'll be able to figure out the sequence of ids from the beginning up to any number, in which case the scheme becomes equivalent to hmac_sha256(auto_increment_number, static_secret), which I think it's just as secure, except for the minor information leak of knowing how many sessions were generated before yours. Besides, you can always just hmac the seed with the same secret.
    – capr
    Commented May 22, 2021 at 10:21
  • @multithr3at3d because if this is secure, then I don't have to use a library, and because the purpose of the question is to try to understand something and using a library would not help me with that (plus I'd need to understand how that library works as well, so I'd be in the same spot).
    – capr
    Commented May 22, 2021 at 10:46
  • As described, I don't see how your scheme couldn't be replaced with sessid = get_secure_random(32)? Stateless implies the client is holding additional session metadata, or that this is the key to some stored state. Please feel free to edit your answer/ elaborate. From an 'is this breakable' perspective: it apparently rests on the entropy introduced by static_secret. See: auth0.com/docs/tokens and csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final
    – brynk
    Commented May 22, 2021 at 20:23

3 Answers 3


I'll assume that you're placing a long-lived token on some client device, then authenticating that device periodically with this.

At the moment, your authentication is stateless and involves the client providing the long-lived token: sid + HMAC( sid , static_secret ). The output of RNG has no bearing on things, because sid must be shared in the clear for stateless authentication to occur.

The security of this basically relies on the ability for an adversary to find static_secret that would then allow them to solve HMAC( sid , static_secret ).

So, the value of static_secret must be high entropy and 256-bit if your hash algorithm is SHA256 Eddie and poncho discussing HMAC key sizes, '18.


What happens when the client device fails or is lost? How does the owner recover the data held by the service? This implies some separate account mechanism. Is there some way you can revoke sid? What if the client device is stolen?

Could devices interact with the server to derive a nonce, that is then used to demonstrate they hold the long-lived token? This would allow them to authenticate without exposing the long-lived token.

You haven't mentioned what transport/ network protocol you're using, and whether the server provides any sort of 'response', or whether the client just sends some payload to server without the expectation of any response.

I still think your device needs to somehow expose time to the stateless auth as well, which implies your client(s) have a reasonably accurate clock, that is relatively in sync with the server.

(A lot of this- where relevant- could be rolled into the authentication token. but it's all still a bit vague: happy to revisit this again if you edit your question to provide some more information.)

  • sorry but I don't see the point in any of this complication that aws does. is there any reason for any of that?
    – capr
    Commented May 24, 2021 at 0:30
  • ...as for putting an expiration time in the cookie, sure, but in my case that would be something like 2 years, so it won't help much, and also that's orthogonal to the question of how secure the cookie itself is, which is what I'm interested in with my question.
    – capr
    Commented May 24, 2021 at 0:36
  • You're correct it isn't specific, and detracts from the answer .. If you could please edit your question to provide more detail on your requirement for stateless authentication then I will edit this answer to make it a lot more specific to your use-case.
    – brynk
    Commented May 24, 2021 at 11:46
  • ps. I still stand by my original comment - the security in the system rests on the entropy in static_secret. Your PRNG can't add more than static_secret, so I've not considered it. (If it does then you need to address this first.) Also, in response to my observation to place a time-limit on things, you mention an estimate of a two-year expiration validity period for the cookie, which has me hooked .. so, it would be good to hear more about that as well!
    – brynk
    Commented May 24, 2021 at 12:42
  • the only reason for using a random number for id instead of an auto-incremented id is simply to hide the information of knowing the sequence of past and future ids. if you don't care about that, you can assume it's an auto-incremented id, just to simplify the discussion.
    – capr
    Commented May 26, 2021 at 10:36

Even when static_secret is completely unpredictable by an attacker (and not reused for authenticating other values than session IDs), your scheme also relies on the uniqueness of session_id.

Imagine a server that sometimes repeats the same session_id. In this case an attacker could log in repeatedly to obtain a large number of session_id's, along with corresponding valid HMACs. Then, when another user logs in and gets assigned the same session-id as previously obtained by the attacker, their session can be hijacked.

A non-cryptographic PRNG does not guarantee collision-free outputs, but the likelihood of 16 bytes being repeated is probably very low for most implementations.

Most non-cryptographic RNGs have a very long period: a Mersenne Twiser, for example, will only start repeating itself after about 10^6000 draws. The largest weak point is probably the fact that the generator is seeded by a timestamp. Consider a case where the web server is replicated and two instances are started at the exact same time. Additionally, the generator may use a non-collision free hash function to turn the timestamp value into an RNG seed.

  • Not sure I get the server replication part. You mean two load-balancing servers that both generate sessions for the same app? In that case, wouldn't it be sufficient to add some static id of the server to the seed?
    – capr
    Commented May 26, 2021 at 17:17
  • thanks for the info on Mersenne Twisters, I didn't know their collision rate.
    – capr
    Commented May 26, 2021 at 17:18

I think what you want achieve is very similar to what JWT does.

From https://jwt.io/:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

Your cookie form session_id+'|'+hmac_sha256(session_id, static_secret) is almost the same as JWT's shown in the image below. In your case, you might put session_id in the PAYLOAD part of JWT.

enter image description here

So I think your cookie form is as breakable as JWT.

However, there are some disadvantages using JWT for sessions:

JSON Web Tokens (JWT) are Dangerous for User Sessions—Here’s a Solution | Redis

There are some ways to tackle those issues as well:

JSON Web Token for Java - OWASP Cheat Sheet Series

I don't really know how you are using the session_id so you should probably check out those links to avoid mistakes.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .