What Encryption Algorithm Is Best for Encrypting Cookies

What encryption algorithm is best for encrypting cookies?

No real reason not to go with AES with 256 bits. Make sure to use this in CBC mode, and PKCS#7 padding.
As you said, fast and secure.

I have read (not tested) that Blowfish may be marginally faster... However Blowfish has a major drawback of long setup time, which would make it bad for your situation. Also, AES is more "proven".

This assumes that it really is necessary to symmetrically encrypt your cookie data. As others have noted, it really shouldnt be necessary, and there are only a few edge cases where there's no other choice but to do so. Commonly, it would better suit you to change the design, and go back to either random session identifiers, or if necessary one-way hashes (using SHA-256).

In your case, besides the "regular" random session identifier, your issue is the "remember me" feature - this should also be implemented as either:

  • a long random number, stored in the database and mapped to a user account;
  • or a keyed hash (e.g. HMAC) containing e.g. the username, timestamp, mebbe a salt, AND a secret server key. This can of course all be verified server-side...

Seems like we've gotten a little off topic of your original, specific question - and changed the basis of your question by changing the design....

So as long as we're doing that, I would also STRONGLY recommend AGAINST this feature of persistent "remember me", for several reasons, the biggest among them:

  • Makes it much more likely that someone may steal that user's remember key, allowing them to spoof the user's identity (and then probably change his password);
  • CSRF - Cross Site Request Forgery. Your feature will effectively allow an anonymous attacker to cause unknowing users to submit "authenticated" requests to your application, even without being actually logged in.

What type of encryption do I need?

First of all, you need to know whether you want to encrypt or sign the data.

Encrypting will prevent users from seeing the data, but they are still able to modify it in some ways depending on the encryption type. For example, decrypting a modified ciphertext will simply give corrupted data, it won't fail.

Signing, on the other hand, will prevent users from modifying the data, that is, your code will be able to detect the data has been modified. A simple algorithm for this is HMAC.

I'll assume you want both. My solution below does both.

Your cookie must be 32 bytes long, which is 256 bits. We are going to use 128 bits for encrypted data and 128 bits for the HMAC.

For the data, I will encode the timestamp as a 64bit integer (more than enough even if you want to store it to microsecond precision). The site that issued the cookie can be stored as 1 bit if you have two sites, but I'll store it in a 32bit integer because we have plenty of space. Same for a tag you can use for a/b testing.

All the data is exactly 128 bits, 16 bytes. This is the exact size of an AES block. So, we will encrypt it with AES!

The other 16 bytes will be a MAC of the ciphertext (Encrypt then MAC). I used HMAC-SHA256, which has 256bits of output. We only have room for 128bits, so I have truncated it. In theory this makes it less secure, but in practice 128bit is big enough to make a brute-force attempt impossible.

Decrypting the cookie is the reverse process: calculate the HMAC of the given ciphertext and check it matches the given MAC. If so, proceeed to decrypt the ciphertext and unpack the data.

Here's the code:

from struct import pack, unpack
from Crypto.Cipher import AES
import hashlib
import hmac

AES_KEY = hashlib.sha256(b"secret key 1 asdfasdf").digest()
HMAC_KEY = hashlib.sha256(b"secret key 2 asdfasdf").digest()

# timestamp: 64bit unix timestamp
# site: 32bit integer, which site issued the cookie
# tag: 32bit integer, tag used for a/b testing.
def encrypt_cookie(timestamp, site, tag):

# Pack the data
data = pack('QII', timestamp, site, tag)

# Encrypt it
aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
ciphertext = aes.encrypt(data)

# Do HMAC of the ciphertext
sig = hmac.new(HMAC_KEY, ciphertext, hashlib.sha256).digest()
sig = sig[:16] # Truncate to only first 16 bytes.

return ciphertext + sig

def decrypt_cookie(cookie):

# Do HMAC of the ciphertext
sig = hmac.new(HMAC_KEY, cookie[:16], hashlib.sha256).digest()
sig = sig[:16] # Truncate to only first 16 bytes.

# Check the HMAC is ok
if sig != cookie[16:]:
raise Exception("Cookie has been tampered with")

# Decrypt it
aes = AES.new(AES_KEY, AES.MODE_ECB, 'This is an IV456')
data = aes.decrypt(cookie[:16])

# unPack the data
timestamp, site, tag = unpack('QII', data)

return timestamp, site, tag

cookie = encrypt_cookie(1, 2, 3)
print(len(cookie)) # prints: 32
print(decrypt_cookie(cookie)) # prints: 1, 2, 3

# Change a single byte in the cookie, the last one
cookie = cookie[:31] + b'0'
print(decrypt_cookie(cookie)) # raises the exception

I'm curious to know why the cookie must be 32bytes though. Seems a weird requirement, and if you didn't have it, you'd be able to use many libraries that are designed to solve exactly this problem, such as Django signing if you're using Django.

Is it necessary to encrypt cookie data if it just stores user preferences?

The answer is a bit more nuanced than what you'd expect ...

Encryption would make the data unreadable, but that's the only thing that bare encryption does; it doesn't actually prevent the data from being modified. What you'd want instead (or in addition to encryption) is message authentication.

These two techniques are often used together (and if you do encryption, you surely do have to do authentication as well), but are slightly different in what they do.

If you don't store any private, sensitive information in the cookie, then you'd probably be fine without hiding (encrypting) it from the user. However, you absolutely MUST implement a message authentication mechanism.

Even if you don't think it is currently a security threat, that might be because you haven't considered all possible attack vectors or you're not actually aware of all of them. And even if it is safe now, that doesn't mean it will be in the future when you or your colleagues add more features or otherwise alter the application's logic.
Therefore, you must never trust unvalidated user input, no matter how, where, when it got into the system or how harmless it may seem at first.

Edit note: Your question doesn't reference PHP, but I assume it as the most popular language for web application development. And I do need some language to produce an example. :)

The easiest way to implement message authentication in PHP is by using the hash_hmac() function, which takes your data, a key and a cryptographic hash function name in order to produce a Hash-based Message Authentication Code (HMAC):


$hmac = hash_hmac('sha256', $stringCookieData, $key);

You can then append (or prepend) the resulting HMAC to your cookie data, which is effectively your "signature" for it:


$stringCookieData = $hmac.$stringCookieData;

And then of course, you'll need to verify that signature when you receive the cookie. In order to do that, you need to re-create the HMAC using the data that you received in the cookie and the same secret key, and finally compare the two hashes:


// 64 is the length of a hex-encoded SHA-256 hash
$hmacReceived = substr($_COOKIE['cookieName'], 0, 64);
$dataReceived = substr($_COOKIE['cookieName'], 64);
$hmacComputed = hash_hmac('sha256', $dataReceived, $key);

if (hash_equals($hmacComputed, $hmacReceived))
{
// All is fine
}
else
{
// ERROR: The received data has been modified outside of the application
}

There are two details here that you need to note here:

  1. Just as with encryption, the key is NOT a password and it must be random, unpredictable (and that does NOT mean hashing the current timestamp). Always use random_bytes() (PHP7) or random_compat to generate a key. You need 32 bytes of random data for SHA-256.
  2. Do NOT replace hash_equals() with a simple comparison operator. This function is specifically designed to prevent timing attacks, which could be used to break your key.

That's probably a lot of info to digest, but unfortunately, implementing secure applications today is very complicated. Trust me, I really did try to make it as short as possible - it is important that you understand all of the above.

What is the common practice for encrypting string in cookie?

beaware: encryption and encoding are 2 different things...

what you posted is a string in base64 encoding which means, once you put that into a base64 decoder, you will get the original string back ... not suitable to keep something secret

if you want just that, here is something for you to read ...

if you want to store some secret, you should think about using some cypher like AES (you can store the binary cyphertext in base64 encoding if you want...)

Encryption on cookie codeigniter 3.0.x

This is not the right way to store the encrypted cookies, Encrypt the value(If necessary) not the entire cookies, Take a look on the codes below

public function store_cookie($email) {

if($this->input->post('remember_me') == 'remember'){
//try email later
$cookie = array(
'name' => 'remember' ,
'value' => $this->encryption->encrypt($email),
'expire' => '86500',
'prefix' => '',
'secure' => FALSE
);
$this->input->set_cookie($cookie);
}
}


Related Topics



Leave a reply



Submit