Secure cookies

Probably every developer who dug in to web applications at least a little bit, probably met cookies. Regrettably, although the cookies are used to store important values, they often not treated accordingly.

The simplest way to store a cookie "mycookie" with "myvalue" value is (in PHP):

setcookie("mycookie", "myvalue");

Due to the way the cookies work, there are two important codes, namely:

  • visibility - For users it's not difficult to read the cookie value, which may not be desirable especially in case of internal values. Storing internal variables in cookies is not a good idea at all, but in some cases it may be handy.
  • modification - Values are stored in user's computer, and the user may modify them without a problem. If the cookie contains value of an internal variable (e.g. a login) this may be a serious security problem.

Let's look at the second problem first, i.e. possibility to modify the values.

Signing of cookies

As the cookies are stored in the user's computer, it's impossible to prevent their modification. The finesse is in detection of the modification instead of futile attempts to prevent it, and then acting accordingly.

Disclosure of the modification is possible thanks to a simple "digital signature" based on hashing (with a secret salt). Instead of storing the simple cookie value (see the first section), you may do this:

$salt = 'jLcQ5a';        // secret values, serving as a password
$value = 'myvalue';      // cookie value
$hash = md5($salt . $value);

// store signed value
setcookie('mycookie', $value . ':' . $hash);

By this a hash is appended to the cookie value, but the user is unable to compute the correct hash if he modifies the cookie value as he does not know the secret value (salt).

When reading the cookie value, we have to verify if the value was not modified:

$cookie = explode(':', $_COOKIE['mycookie']);

$salt = 'jLcQ5a';
$hash = md5($salt . $cookie[0]);   // proper value

// verification of the value
if ($hash == $cookie[1]) {
    // the value was not modified - load the value
    $value = $cookie[0];
} else {
    // the value was modified - logout
    header('Location: /logout');
    exit();
}

Thanks to this (somehow primitive) version of a digital signature, you may be sure the cookie value was unchanged. If needed you may add user identification (e.g. ID, login, ...) the hash, so that the "signatures" are unique for each user, so it's not possible to use a cookie created for one user for another one.

Enciphering cookies

The next step in protecting cookies, solving the visibility problem, is encryption of course. Using the mcrypt library may be done for example like this:

$type = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_ECB;
$source = MCRYPT_RAND;

$iv_size = mcrypt_get_iv_size($type, $mode);
$iv = mcrypt_create_iv($iv_size, $source);
$key = 'H90uAcV';
$value = 'myvalue';

$cryptvalue = mcrypt_encrypt($type, $key, $value, $mode, $iv);

When loading the values you should proceed in the other direction, i.e. decipher the value using mcrypt_decrypt function.

Note: Encryption of cookies is not a protection from "man in the middle" attacks. Cookies are encrypted and thus secure, but the attacker does not need to decipher them and the remaining data is not enrypted.

Encryption by itself does not prevent the user from modifying the value - he may modify the value blindly and hope that after decryption the value won't be garbage. The only option is combining the two practices (encryptiong and signing), and it doesn't matter which of them is applied first - I personally do the signing first.

$type = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_ECB;
$source = MCRYPT_RAND;

$iv_size = mcrypt_get_iv_size($type, $mode);
$iv = mcrypt_create_iv($iv_size, $source);
$key = 'H90uAcV';
$value = 'myvalue';

$value = $value . ':' . md5($key . $value);
$cryptvalue = mcrypt_encrypt($type, $key, $value, $mode, $iv);

And when reading the cookie value, you need to proceed in the reverse order - decryption first, signature verification second.

Comments

There are no comments for this article (or are awaiting acceptance).

New comment

All the comments have to be accepted, so there may be some delay between submitting and accepting (or rejecting) the comment. If you enter the e-mail address, you will be informed about acceptance or rejection.

Subject or body may not contain HTML tags - they will be automatically removed. Paragraphs may be separated using a newline (ENTER).

(optional)