42

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.

NOTES

The most recent material I have read:
Dos and Don'ts of Client Authentication on the Web aka Fu et al.
(https://pdos.csail.mit.edu/papers/webauth:sec10.pdf)

A Secure Cookie Protocol aka Liu et al.
(http://www.cse.msu.edu/~alexliu/publications/Cookie/cookie.pdf)
which expands on the previous method

Hardened Stateless Session Cookies
(http://www.lightbluetouchpaper.org/2008/05/16/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.

10
  • Also on StackOverflow stackoverflow.com/questions/4000184/…
    – Joony
    Feb 11, 2013 at 14:28
  • 3
    Please, please please do not cross-post. Flag for migration instead.
    – user10211
    Feb 11, 2013 at 14:30
  • Why are you nesting your MAC? Feb 11, 2013 at 14:38
  • Sorry, I've flagged the StackOverflow question for moderation.
    – Joony
    Feb 11, 2013 at 14:39
  • 3
    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. Feb 11, 2013 at 14:40

4 Answers 4

37

"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.

2
  • 2
    would that be the same as using a JWT inside a cookie?
    – hytromo
    Sep 27, 2016 at 17:00
  • 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? Apr 5, 2020 at 19:47
14

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.
2
  • 1
    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
    Feb 11, 2013 at 15:27
  • 1
    @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). Feb 11, 2013 at 16:04
8

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.

1
  • 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?
    – Joony
    Feb 11, 2013 at 15:21
3

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.

1
  • 6
    That's all fair and well, but what solutions exist for stateless session cookies, and what security do they offer?
    – Joony
    Feb 11, 2013 at 15:17

You must log in to answer this question.

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