3

I have a scheme for a cookie with high-level confidentiality of information. The information to be hidden from the client is the expiration time of the cookie. I am wondering what insecurities i am opening myself up to with this scheme.

Note below that | denotes concatenation and (text)k is text encrypted using key k.

cookie = creation_time | (expiration_time)k | HMAC(creation_time|expiration_time|server_key , k) where k = HMAC(creation_time, server_key)

Steps for key validation are:

  1. Ensure creation_time is valid time stamp
  2. create k = HMAC(creation_time, server_key)
  3. decrypt (expiration_time)k using k.
  4. ensure (expiration_time - creation_time <= T) && current_time < expiration_time where T is a server allowed TTL for a cookie.
  5. compute HMAC(creation_time | expiration_time| server_key, k) and compare against the cookie value. if they are the same the cookie is valid.

The whole point of this cookie is to allow me ensure a cookie is received by the user at one point and pinged back through ajax within a time T.

lluisrojass
  • 131
  • 1
  • I don't quite understand the need for all the effort? Why not just set the equivalent of a session id in the cookie, store its creation time on the server, and then validate the expiration time based on the server's own record of when the cookie was created? Why do you have to store the creation time in the cookie at all? – Conor Mancone Aug 18 '17 at 20:13
  • @ConorMancone i'm trying to accomplish this without needing server side storage. the client i'm doing this for has a shared hosting plan and so i cannot use distributed caching systems like redis. otherwise it would be simple. – lluisrojass Aug 18 '17 at 20:16
  • @lluisrojass You don't need storage for this approach; just make the expiration part of the session variables (available in every modern web language!) – Polynomial Aug 18 '17 at 20:19
  • the cookie i'm creating is to start a session. previously i have a PHP Session created everytime a new user GETs /index.php. My client has limited hosting space and so i'm trying to prevent someone cURL requesting every 2 seconds and just eating up space. So now an initial request to index.php kicks off an ajax ping back with this cookie. this way i can better ensure the user is legitimate. – lluisrojass Aug 18 '17 at 20:30

1 Answers1

1

There are a few things wrong with this construction.

You didn't define which encryption algorithm you're using. If you were to use a block cipher mode with padding, such as CBC, your design would be potentially vulnerable to a padding oracle attack, since your server must decrypt the encrypted expiration_time value before validating the authenticity record against the plaintext. You should apply authenticity checks against the ciphertext wherever possible.

It's also not clear how the delimiters work between fields. You should define how each individual field is separately produced to avoid any confusion on the decoding process.

While not critical, it's also bad practice to have a single shared key for both encryption/decryption and authenticity (HMAC). You should always aim to have independent keys for these.

All in all this is an overly complex design.

A simpler way is to just store the expiry time as a session variable, like this in PHP:

session_start();
$_SESSION['expiry'] = time() + 60 * 60 * 24; // now +24 hours

And then in each request:

session_start();
if (!isset($_SESSION['expiry']) || !is_int($_SESSION['expiry']))
{
    // expiry time has been unset or corrupted somewhere, log and fail
}
if ($_SESSION['expiry'] < time())
{
    // session expired, do whatever you need to do about this
}

This saves the complex cryptographic scheme and the client-side access to the token.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • I am trying to implement this cookie to facilitate the creation of a valid session. My client has limited space, and currently i have a session being created every time a new user GETs /index.php. To prevent someone cURL requesting every 2 seconds and eating up space, what i want to do is return index.php and have it ajax ping back with this cookie, if the cookie is valid it creates a session, and returns the PHPSESSID, if not then it does not. – lluisrojass Aug 18 '17 at 20:43
  • 1
    Then this is an XY problem. You should have asked how to avoid a disk exhaustion DoS attack in PHP. – Polynomial Aug 18 '17 at 20:46
  • i'm sorry :(, do you have any advice as to how to mitigate this issue? – lluisrojass Aug 18 '17 at 20:52
  • @Polynomial indeed. I'm not sure if this is the way to prevent DoS either. This may prevent some stuff being written to the hard-drive, but encryption/decryption can be CPU intensive. As a result you may be trading a space-eating DoS "attack" for a CPU eating DoS "attack". Either way your server may die – Conor Mancone Aug 18 '17 at 20:52
  • In that case though why bother encrypting anything? Just store the expiration time in the cookie in plain text and check it server side. If an attacker is mucking with the expiration time in the cookie then you have someone executing a targeted attack against your site. In that case they will find the least common denominator, and they will find a more effective way to take down a VPS site in a shared environment than trying to pick apart your cookie. – Conor Mancone Aug 18 '17 at 20:54
  • Yeah, I made the assumption that encryption was for a solid reason. Sounds like `expiry+HMAC(expiry)` alone would work, although that doesn't stop someone from just dropping the cookie for each request anyway as if it were their first visit to the site, so it doesn't fix the session DoS issue at all. – Polynomial Aug 18 '17 at 20:56
  • 1
    @lluisrojass Rate limiting in mod_security is a good start. If you've got access to iptables rules you could set those up as another layer of rate limiting. After that you can set `session.gc_maxlifetime` to a reasonable value to try to ensure session files are cleaned up quickly when unused. – Polynomial Aug 18 '17 at 20:58
  • @Polynomial To be clear, I wasn't criticizing your answer at all: I 100% agree that this is an XY issue. I was just further musing about the ways in which the implementation in the original question probably doesn't solve the underlying issue, even if properly implemented. – Conor Mancone Aug 18 '17 at 20:59
  • @ConorMancone Yup, I know :) – Polynomial Aug 18 '17 at 20:59
  • @lluisrojass You could also set up a cron job which checks the size of the session storage directory and, if larger than a set amount, loops through all sessions defined in there, calling `session_id($id)` on each, checking to see if that session has any useful information in it (e.g. a logged in user) and, if not, calling `session_destroy()`. – Polynomial Aug 18 '17 at 21:02
  • `As a result you may be trading a space-eating DoS "attack" for a CPU eating DoS "attack".` Well if someone creates a valid session, it's about 20 minutes before it gets considered garbage and can be deleted, but i can mitigate a CPU eating ddos much easier. – lluisrojass Aug 18 '17 at 21:28