views:

94

answers:

5

I am thinking about making the switch to storing session data in encrypted cookies rather than somewhere on my server. While this will result in more bandwidth used for each request - it will save extra database server load and storage space.

Anyway, I plan on encrypting the cookie contents using RIJNDAEL 256.

function encrypt($text, $key) 
{
    return mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$key,$text,MCRYPT_MODE_ECB,mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256,MCRYPT_MODE_ECB),MCRYPT_RAND)); 
}

Which in use would produce something like this (base64 encoded for display)

print base64_encode(encrypt('text', 'key'));

7s6RyMaYd4yAibXZJ3C8EuBtB4F0qfJ31xu1tXm8Xvw=

I'm not worried about a single users cookie being compromised as much as I am worried that an attacker would discover the key and be able to construct any session for any user since they know what I use to sign the data.

Is there a way I can verify estimated cracking times in relation to the parameters used? Or is there a standard measure of time in relation to the size of the text or key used?

I heard someone say that the keys needed to exceed 256bits themselves to be safe enough to be used with RIJNDAEL. I'm also wondering if the length of the text encrypted needs to be a certain length so as not to give away the key.

The data will generally be about 200 characters

a:3{s:7:"user_id";i:345;s:5:"token";s:32:"0c4a14547ad221a5d877c2509b887ee6";s:4:"lang";s:2:"en";}

So is this safe?

+1  A: 

Rijndael was renamed AES. Yes, it is safe to use.

That said, you should consider carefully what you put in the cookie. It depends on what you have available in the way of storage on your system, but you could simply choose a random number (say a 64-bit number), and store that in the cookie. In your server-side system, you'd keep a record of who that number was associated with, and the other details. This avoids encryption altogether. You use the other details to validate (to the extent anything can be validated) whether the cookie was sent back from the browser you originally sent it to.

Alternatively, you can use a different encryption key for each session, keeping a track of which key was used with which session.

Even if you go with straight encryption with a fixed key, consider including a random number in with the data to be encrypted - this makes it harder to crack using a known plaintext attack because, by definition, the random number can't be known.

Jonathan Leffler
AES is a special case of Rijndael. AES has a fixed block length of 128 Bit, Rijndael has a variable block length of 128, 192 or 256 Bit.
Juri Robl
@Juri: AES-128 is Rijndael-128; AES-192 is Rijndael-192; and (surprise, surprise), AES-256 is Rijndael-256. All the AES contenders had to provide a 128-bit block size and keys in lengths of 128, 192, and 256 bits.
Jonathan Leffler
@Jonathan: Yes, that's right, but Rijndael (128 to 256) still has variable block length, whereas AES has not.
Juri Robl
+1 for the good idea of placing a random number in there just because it adds complexity.
Xeoncross
+2  A: 

Avoid using ECB. It can reveal information about what's encrypted. Any two blocks with the same plaintext will have the same ciphertext. CBC would avoid this, but requires an IV to be generated or saved.

Avoid simply saving a key and IV. Generate a 256 bit master key using a cryptographically strong random number generator and save that into you application somewhere safe. Use that to generate session keys for use in encryption. The IV can be derived from the session key. When generating the session key include any and all available data that can be used to narrow the scope of the session key. (e.g. include the scope the cookie, the remote host address, a random nounce stored with the encrypted data, and/or a user ID if it isn't within the encrypted data)

Depending on how the data is to be used you may have to include a MAC. ECB and CBC are not designed to detect any changes to the ciphertext, and such changes will result in garbage in plaintext. You might want to include an HMAC with the encrypted data to allow you to authenticate it before taking it as canon. A session HMAC key must be derived from the session encryption key. Alternatively, you could use PCBC mode. PCBC was made to detect changes in the ciphertext, but its ability to do so is limited by the size of the padding, witch is dependent on the data that is encrypted, and not all crypto APIs will have it as an option.

Once you have gone so far as to include a MAC, then you should consider taking steps against replay attacks. Any time someone can resend old data within the scope of a session is a chance for a replay attack. Making a session key usage as narrow as possible without causing issues for the user is one way to thwart replay attacks. Another thing you could do is include a date and time into the encrypted data to create a window for while the data is to be considered valid.

In summery, protecting the key is just the tip of the iceburg.

Lunatic Experimentalist
After generating a IV I appended it onto the encrypted text. Then I created a SHA256 HMAC (from the same key used to encrypt the text + the encrypted text). So when receiving data I now check the HMAC - and if valid then proceed to decrypt the data. Also, inside the data I have a timestamp so I can verify the data is not only *correct and sent by me* - it is sent in a timely manner.
Xeoncross
A: 

AES-128 should be more than sufficient, with no needs to use longer keys - if the key is chosen randomly.

However there are other issues. The first is that you should not use ECB. With ECB a given 128-bit block of plaintext always maps into the same 128-bit ciphertext if the key is the same. This means that adversaries can surgically modify the ciphertext injecting different blocks for which they know the corresponding ciphertext. For example they could mix the data of two different users. With other modes, CBC for example is fine, the ciphertext also depends on the IV (initialization vector), which should be different at every execution of the algorithm. This way, the same plaintext is ciphered differently each time and the adversary cannot gain any advantage. You also need to save the IV somewhere with the ciphertext, no need to protect it. Whenever the chance of reusing the same IV becomes non-negligible you should also change the key.

The second issue is that you should also append a message authentication code. Otherwise you would not be able to distinguish the forged cookies from the good ones.

Giacomo Verticale
+1  A: 

If you use a long key, I'd say the key was pretty safe. Some things to concern yourself with:

You are offloading data storage to the client. NEVER TRUST THE CLIENT. This doesn't mean you can't do this, just that you either have to treat the data in the cookie as untrusted (don't make any decisions more serious than what 'theme' to show the user based on it) or provide for a way to validate the data.

Some examples of how to validate the data would be to:

  • include a salt (so that people with the same session data don't get the same cookie) and
  • a checksum (so that someone who changes even one bit of the cookie makes it useless).
Slartibartfast
Using a HMAC and CBC mode instead of ECB solves both of these.
Xeoncross
+2  A: 

Yes Rijndael(AES) is safe, however your implementation is far from safe. There are 2 outstanding issues with your implementation. The use of ECB mode and your IV is a static variable that will be used for all messages. An IV must always be a Cryptographic Nonce. Your code is in clear violation of CWE-329.

ECB mode should never be used, CBC mode must be used and this why:

Original:

alt text

Encrypted with ECB Mode:

alt text

Encrypted using CBC mode:

alt text

Rook