8

I'm currently helping write a fast compiled web framework in the crystal language and am trying to find a way to speed up sessions using encrypted cookies. We're currently taking a json string and encrypting it with AES. We're then base64 encoding and signing it.

My question is: Is it necessary to sign or will a modified encrypted string fail to decrypt? If it doesn't result in valid json which can be parsed into a session it will be empty in the same way that it would with an invalid signature. My understanding is that padding oracle attacks work by getting feedback from the server. Would an encrypted empty json string be enough?

  • It depends on your implementation details. – Jacco Aug 22 '17 at 8:10
  • 1
    Encryption is generally not trustworthy without authentication, and with AES encryption, a string decrypted with a different key is extremely unlikely to parse as valid JSON, but this is irrelevant. Your encrypted blob should always be signed if you want to be certain the plain text was encrypted with your key. I'm not sure about the Oracle padding attack myself, hence the comment. – Nick Bedford Aug 23 '17 at 0:22
  • AES only encrypts one block of 128 bits, no more, no less. Thus, what mode of operation do you use with AES? How do you manage the keys? Are you sure using a big (encryted) cookie will not slow down the websites compared to using a database with a fast caching solution? – A. Hersean Aug 23 '17 at 7:50
  • 1
    So you're sending the session to the client? Why not server-side sessions like PHP using only a Session ID as the key into the server-side data? I may be misinterpreting your post. – Nick Bedford Aug 24 '17 at 6:44
  • 1
    Encrypting and signing cookies every request sounds like more work than server sessions. What type of data do you need to store in sessions and cookies and not a database entry? If data is large enough, storing it in a database, even if temporarily, seems like a straight forward solution. Database tables can be pruned with ease on a schedule. – Nick Bedford Aug 25 '17 at 3:50
3
+50

The short answer is that you cannot rely on encryption to ensure the integrity of a message. See here for example.

To demonstrate why that general statement is also true for the specific example of this question, let's see how an attacker can modify an encrypted message. For the sake of simplicity I'm going to assume you're using AES-CBC encryption. Let's consider the following string:

{"Name":"Ryan Archer","Crime":"First Degree Murder","Judge 1":"Afred E Newman Jr","Verdict":"Not Guilty"}

When one AES-CBC encrypts the above string it will be encrypted in the following 16-byte blocks:

Block 0:   "Name":"Ryan Arc
Block 1:   her","Crime":"Fi
Block 2:   rst Degree Murde
Block 3:   r","Judge 1":"Al
Block 4:   fred E Newman Jr
Block 5:   ","Verdict":"Not
Block 6:   Guilty" 

And each block of the encrypted string will correspond with the the same block of the original cleartext string.

Now here's an interesting characteristic of CBC. Even if you don't know the encryption key you can modify the encrypted text in a way that when it is decrypted the clear text will be modified in a specific way.

Let's take the example above. If someone was to AES-CBC encrypt this string with some key and I don't know this key, if I XOR the last 3 bytes of block 4 of the encrypted text with the string "Not" then the impact on the decrypted text would be as follows:

  • Block 4 would be corrupted in an unknown random way.
  • The last 3 bytes of block 5 would be XORed with the string "Not"

So after decryption the string would read as follows:

Block 0:   "Name":"Ryan Arc
Block 1:   her","Crime":"Fi
Block 2:   rst Degree Murde
Block 3:   r","Judge 1":"Al
Block 4:   %$#$%@#%$@#%#%#$ (random garbage bytes)
Block 5:   ","Verdict":"
Block 6:   Guilty" 

And would be read as the following valid json string.:

"Name":"Ryan Archer","Crime":"First Degree Murder","Judge 1":"Al%$#$%@#%$@#%#%#$","Verdict":" Guilty"

The judge's name is randomly corrupted, but the attacker has successfully modified a critical portion of the message - Ryan Archer is now guilty of murder!

Of course not all strings are structured in such a way and it is possible that the random bytes in block 4 will corrupt the string so that it is no longer a valid json string. But an attacker can try this attack multiple times with various modifications of the encrypted string and sooner or later will get a valid json string.

3

Is it necessary to sign or will a modified encrypted string fail to decrypt?

In the cryptography world a modified message is called a tampered message. Encryption by itself does not provide protection against tampering, but block ciphers are not used alone. Block ciphers are used in different modes, f.e. ECB, CBC, CTR, GCM, etc. Different modes have different properties, some of them provide protection against tampering (a.k.a. Authenticated Encryption, f.e. GCM) and others not.

If you're using an Authenticated Encryption mode a tampered message will fail to decrypt. But, if you're using others modes like CBC or CTR (These are the most widespread in my experience) a tampered message will happily decrypt unless there is a padding error

If it doesn't result in valid json which can be parsed into a session it will be empty in the same way that it would with an invalid signature.

This will depend on how your application handles an invalid json, and here is where a padding oracle may appear.

My understanding is that padding oracle attacks work by getting feedback from the server. Would an encrypted empty json string be enough?

A padding oracle attack occurs when the attacker sends a tampered message and is able to distinguish if the padding is correct or is not. And this is why I said previously that how your application handles an invalid JSON may derive into a padding oracle

Imagine that an attacker sends a tampered message with a wrong padding, your application will try to decrypt the message but a wrong padding exception will throw. After that the attacker tampers the message again but this time he's able to forge a correct padding, your application will decrypt correctly the message but the content will be and invalid JSON, the application will attempt to parse the invalid JSON and throw an error as it can't be parsed

If the attacker is able to distinguish both scenarios cause the HTTP status returned is different, or a stacktrace shows the different error messages, or just by timing the response time. Then the attacker has a padding oracle and can recover the plaintext from the cookie

To prevent this you should always provide same error message when the message can't be decrypted and when the message decrypts but the content is invalid. Furthermore you should ensure that both scenarios require the same time to respond.

IMO the best approach would be to use an authenticated encryption mode that gives you encryption and signing together and let the encryption framework/library handle it

BTW, I mentioned an encryption mode that should never be used. It's ECB, if you're using this mode (Some libraries default to it) please change it as it's a weak mode

  • What libraries default to ECB? They should be very old / broken. o-O – goteguru Aug 29 '17 at 11:11
1

Is it necessary to sign or will a modified encrypted string fail to decrypt?

The classical (and faster) way to authenticate your message is using MAC. You should use signing if you need non-repudiation (which you probably do not need).

If it doesn't result in valid json which can be parsed into a session it will be empty in the same way that it would with an invalid signature. [...] padding oracle attacks work by getting feedback from the server. Would an encrypted empty json string be enough?

Regarding padding oracle attack:

AES is a block encryption. There is no padding in AES. If your implementation uses vulnerable padding (like PKCS7 does) padding oracle attack might be possible in theory. However, AES is a CBC encryption (starting with a random mess) which means the same plaintext will result in different ciphertext every time. It would be very difficult for the attacker to guess you have returned an empty json string.

However, it might be possible to detect the padding error by other means. If you are using different HTTP error codes in your REST implementation, or by measuring the server response time. MAC will help here, since the attacker can not tamper with your message. You will drop the message immediately if the MAC is invalid and decryption never takes a place.

You may want to include random timing to the response to make it harder to profile your answers and/or return the same http code for everything to further increase security, but I think the latter would cause more problems than the gain. Of course, you shouldn't reveal that you've detected a padding error.

  • 1
    CBC is an encryption mode that allows to use a block cipher to encrypt multiple blocks. AES is a block cipher that can be used in CBC mode, but other modes exist and other block ciphers can be used in CBC mode – Mr. E Aug 27 '17 at 0:18
  • Yes, this is the precise phrasing. The core of the answer still applies I hope :) CBC is very likely here. – goteguru Aug 29 '17 at 11:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.