Demystifying Web Authentication (Stateless Session Cookies)

  • I'm currently researching user authentication protocols for a website I'm developing. I would like to create an authentication cookie so users can stay logged in between pages.

    Here is my first bash:

    cookie = user_id|expiry_date|HMAC(user_id|expiry_date, k)

    Where k is HMAC(user_id|expiry_date, sk) and sk is a 256 bit key only known to the server. HMAC is a SHA-256 hash. Note that '|' is a separator, not just concatenation.

    This looks like this in PHP (Note, this question is language-agnostic, PHP is just an example):

    $key = hash_hmac('sha256', $user_id . '|' . $expiry_time, SECRET_KEY);
    $digest = hash_hmac('sha256', $user_id . '|' . $expiry_time, $key);
    $cookie = $user_id . '|' . $expiry_time . '|' . $digest;

    I can see that it's vulnerable to Replay Attacks as stated in A Secure Cookie Protocol, but should be resistant to Volume Attacks, and Cryptographic Splicing.

    THE QUESTION: Am I on the right lines here, or is there a massive vulnerability that I've missed? Is there a way to defend against Replay Attacks that works with dynamically assigned IP addresses and doesn't use sessions? I don't want to be storing anything on the server-side to have this work.

    Also, I'm not planning or rolling my own. I'm asking this to better judge what solution I should choose. So no "Just use X solution" answers without some sort of explanation.


    The most recent material I have read:
    Dos and Don'ts of Client Authentication on the Web aka Fu et al.

    A Secure Cookie Protocol aka Liu et al.
    which expands on the previous method

    Hardened Stateless Session Cookies
    which also expands on the previous method.

    As the subject is extremely complicated I'm am only looking for answers from security experts with real world experience in creating and breaking authentication schemes.

    Please, please please do not cross-post. Flag for migration instead.

    Why are you nesting your MAC?

    Sorry, I've flagged the StackOverflow question for moderation.

    Do you consider an SSL connection a session? SSL prevents replay attacks, and prevents attackers from learning your secret. If you can use SSL, just set the `secure` and `httponly` flags for your cookie.

    Do you have a good reason for using stateless sessions? Having session tokens is a better solution in most situations.

    @CodesInChaos The nested MAC is covered in "A Secure Cookie Protocol" section 2.2. It states that it would otherwise be susceptible to cryptanaysis.

    IMO that paper is weird. It's far too concerned about attacks that require huge breaks against the crypto primitives and adds needless complexity to guard against these attacks. It also uses the same key in HMAC and for encryption, which is bad style.

    Ugh, looks terrible! I'm very sorry, but what's the problem with storing the authentication state server-side and just having a session ID client side? Using SSL, and a HTTPS-only session cookie - where or what's the issue? Any solution where anything from the client is trusted, feels bad.

    Hello. I am having the same question as you had. Can you share what solution you came up with? Thanks!

  • "Replay attacks" don't really apply to cookies, because a cookie is by definition something which is meant to be replayed: the user's browser sends back the same cookie value, and that is how your server knows that it is the same user.

    What you want to avoid is someone spying on the line, observing the cookie value, and then sending the same cookie on his own connections. The only known practical solution for that is to run the whole protocol inside a tunnel which ensures confidentiality and integrity of data in transit, and that's known as "SSL" (aka HTTPS).

    Then there are two ways for generating and verifying the cookie values on the server:

    • You can just generate a random value of sufficient size (i.e. 16 bytes or so), so that it would be highly improbable for an attacker to guess the value out of sheer luck. The server remembers for each user his current "session token", as an extra column in the "users" database.

    • You produce the token as an unforgeable value deterministically computed from the user identifier. That's what you do, and that's a reasonable concept. Basically, instead of storing some extra state on the server, you offload it on the client, and you use a MAC with a server-side key so that clients cannot forge "valid" cookie values.

    Your code describes two nested HMAC calls, which is weird and unwarranted. One call is sufficient. The generic construction is that a cookie contains a value v and a MAC m such that v encodes the state you wanted to store in the client, and m is computed over v. The state you want to store, in your case, is: "this client has user ID i and logged on at time t". You can use whatever encoding you wish provided that you can decode it unambiguously.

    To sum things up: producing cookie values with a MAC is a valid way of doing session management, and HMAC/SHA-256 is good for that. But you must think of it as an optimization: you store values in the client to avoid storing them on the server. This has the unfortunate consequence that the client can forget data out of your control (the client may destroy the cookie) or can do time warps (the client may change the value stored in its browser to an older cookie value -- an issue which is mitigated by including dates in the cookies, under control of the MAC).

    If you can keep state server side and just use random cookie values, then do it: it is simpler and gives more control to the server.

    would that be the same as using a JWT inside a cookie?

    What if I generate a cryptographically secure 100 character url safe token on the server side, and also generate a HMAC of it and set this token, digest pair as a cookie on the client? Is that overkill? Does this add any security or is it just unnecessary?

  • The first step in securing any web application is using SSL. That keeps your cookie confidential, prevents replay attacks, ensures the user is talking to the right server, prevents MitM, prevents attackers from changing the data on the network...

    Then set the secure flag on the cookie, so it's only sent over SSL. Setting the http-only flag, to prevent javascript from reading it doesn't hurt either.

    Finally concerning your cookie scheme:

    • The nested HMAC doesn't gain you anything. Just go with user_id|expiry_date|HMAC(user_id|expiry_date, SECRET_KEY)
    • Change SECRET_KEY regularly
    • The user_id must never change. i.e. use the numeric user_id, not the username or email
    • Neither user_id nor expiry_date should ever contain a | to ensure clean domain separation
    • If you're paranoid, use a specialized login server, that signs the cookie instead of MACing it. So the login server is the only server that has the key required to create cookies, normal webservers can only validate it. => reduces attack surface
    • The server can create valid cookies for any user. That might be undesirable.

    Apparently, according to A Secure Cookie Protocol section 2.2 the nested HMAC is to better defend agains cryptanalysis. I like the specialized login server and using SSL.

    @Joony My impression is that they only did it because they're afraid of authenticating a lot of data under the same key, which IMO is unnecessary as long as your primitive if good (HMAC-SHA-2 is).

  • First and foremost:

    Do not invent your own security protocols. You're very likely to get it wrong.

    Now that's out of the way, let's look at this. If I can ever get HMAC(userid|expiry_date, k), without logging in or having authenticated as that user, I have broken the security of this system. This extends to all manner of issues, such as session-fixation or session-hijacking attacks (often caused by not, or incorrectly, using SSL/TLS).

    When you're checking the session cookie, are you leaking information from timing side-channels? Are you definitely using HMAC correctly? You said your HMAC is SHA-256, do you actually mean HMAC-SHA-256? Why go through a complicated HMAC procedure, when you could just generate a random string and associate it with a user on the server (for example in a database)? Why not just use user_id|expiry_date|HMAC-SHA-256(user_id|expiry_date, sk) (where | is concatenation, not the literal pipe character)?

    As for defending against replay attacks in a web-based environment, look into OWASP's CSRFGuard project. You won't be able it directly integrate it, but there may be a PHP equivalent somewhere. It is all for moot if your site is vulnerable to XSS attacks, however.

    Security should be handed off to a respected, battle-tested framework, not done ad-hoc

    Edit, a brief word on session fixation attacks:

    I assumed that if you're not handing security to a well-tested framework, you're probably vulnerable. You'll have to basically check that you're issuing a distinct session key when the user authenticates (i.e. different to the one which the user started with). With the above strategy, user_id|expiry_date|HMAC(user_id|expiry_date, k) would not change when the user authenticates, leaving them vulnerable to a session fixation attack.

    I didn't invent it, and I'm not planning on rolling my own. I would very much like to be able to evaluate better the existing security solutions. Is session-fixation relevant with this solution?

  • As the subject is extremely complicated I'm am only looking for answers from security experts with real world experience in creating and breaking authentication schemes.

    You are right that the subject is extremely complicated.

    Go with the session functions provided by your framework and don't try to roll your own. If you are using plain PHP instead of a framework... shudders.

    That's all fair and well, but what solutions exist for stateless session cookies, and what security do they offer?

License under CC-BY-SA with attribution

Content dated before 7/24/2021 11:53 AM