Ayende @ Rahien

It's a girl

Hiding values, API keys and other fun stuff

This post is mostly about fun ideas. In one scenario, we had the need to show data to the user, but there was some concern with regards to the hackability of the URL.

In general, you should be handle such things within your code, checking permissions, etc. But I decided to see if I can do something nice with things, and I got this:

private static object HideValues(string entityId, string tenantId, byte[] key, byte[] iv)
{
    using (var rijndael = Rijndael.Create())
    {
        rijndael.Key = key;
        rijndael.IV = iv;
        var memoryStream = new MemoryStream();
        using (var cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write))
        using (var binaryWriter = new BinaryWriter(cryptoStream))
        {
            binaryWriter.Write(entityId);
            binaryWriter.Write(tenantId);
            binaryWriter.Flush();

            cryptoStream.Flush();
        }
        var bytes = memoryStream.ToArray();
        var sb = new StringBuilder();
        for (int index = 0; index < bytes.Length; index++)
        {
            var b = bytes[index];
            sb.Append(b.ToString("X"));
            if (index % (bytes.Length/4) == 0 && index > 0)
                sb.Append('-');
        }
        return sb;
    }
}

This will generate a “guid looking” value that we can send to the user. When they send it back to us, we can decrypt it and figure out what is actually going on in there.

Because it is encrypted, we know that this is a valid key, because otherwise we wouldn’t be able to decrypt it to valid data.

Passing 15 and 32 as the first two values, I got the following value back: 2A8AC8888-46B92092-BFD81393-7A6FB1

And it handle larger values as easily, of course. Quite fun, even if I say so myself. Not sure if this is useful, but I got into writing code because it is a great hobby.

Comments

John Downey
04/09/2012 10:45 AM by
John Downey

Be very careful about encrypting data without providing any integrity protection. If you don't integrity protect the encrypted value, with an HMAC signature for example, then attackers can intelligently modify the encrypted value and look at indicators (error codes, timing, etc) to leak the input plaintext. This is the same problem which led to the padding oracle issue found in ASP.NET and other frameworks a few years back.

Ayende Rahien
04/09/2012 10:47 AM by
Ayende Rahien

John, How would you use HMAC to secure things in this case?

andres
04/09/2012 10:50 AM by
andres

Maybe it is more secure to create a random url for each shared resource.

Ayende Rahien
04/09/2012 10:57 AM by
Ayende Rahien

Andres, This is security by obscurity, you can't decide to remove one particular value, etc.

The Thing
04/09/2012 11:11 AM by
The Thing

I think if you saw this in someone else's could we would be treated to a long blog post detailing all the things wrong with it.

Ayende Rahien
04/09/2012 11:13 AM by
Ayende Rahien

The Thing, One of the reason that I am posting this is to GET your feedback. Feel free to post about all the things broken with it.

Andres
04/09/2012 11:25 AM by
Andres

Andres, This is security by obscurity, you can't decide to remove one particular value, etc.

I am not sure that you have understand me, I have being talking about to store in a database a relation of entityId and tenantId with a totally random guid, for example 15 and 32 with 280b7c8b-e590-4671-8c69-ef25955eaa3f.

I know that it requires more logic and it is not as functional as your design, but it is totally unbreakable and you have a great granularity if you want to allow, disallow or change one single resource access.

Thomas Krause
04/09/2012 12:01 PM by
Thomas Krause

As John mentioned, you didn't include any kind of integrity check in your code. The question is: Do you want to hide the entity ID and tenant ID from the user or do you want to just prevent tampering with them?

In the second case you can use a standard HMAC mechanism (compute checksum and encrypt this checksum). In the first case you can do the same and then encrypt the message as a whole again like in your code.

Another problem of this approach is that it's prone to message repeat attacks. Once an attacker knows, that a specific tenant+entity id results in string X, he can always send this string X (think man in the middle attack).

So it would be nice to add a changing value to the mix which you can also check.

Itamar
04/09/2012 12:04 PM by
Itamar

I actually did this once myself, redirecting users from non-secure product page to a secured order page on another domain, in another application (long story short: keeping cert payment to the bare minimum).

This is a much better solution than saving session states in a database, and it makes sure the redirect never expires (a good thing for us), but I never liked that solution. Maybe because this is basically an ASP.NET WebForms ViewState.

And you want to salt this.

tobi
04/09/2012 01:41 PM by
tobi

I think you can't tell if the data sent is actually valid because any 16 byte value should decrypt to some (random) plaintext. A cipher is just a permutation of all possible 16 byte values. People could make your app believe that there was some tenant with id "65&/$%bnsdjg" for example.

Replay attack possible here. No way to revoke a guid once handed out. Can be stolen.

John Downey
04/09/2012 01:51 PM by
John Downey

The easiest thing would be to run the encrypted data through HMACSHA1 with a different key than the one used for encryption. You would then append that to the data being sent to the user. In C# it would look something like:

byte[] signature = new HMACSHA1(key).ComputeHash(data);

When the user returns be sure to validate the MAC first and reject any ciphertext that is not properly signed before ever attempting to decrypt it. You should also be aware that == will likely leak timing information during MAC verification[1].

[1] http://codahale.com/a-lesson-in-timing-attacks/

Guy Mahieu
04/10/2012 09:01 AM by
Guy Mahieu

We actually use this same principle to avoid ppl tampering with urls containing details about a payment. We decided to use cipher block chaining as well to patterns in the URLs for similar parameters.

Guy Mahieu
04/10/2012 09:05 AM by
Guy Mahieu

Sorry, I looked over the init vectors part of your code before when I wrote my CBC remark... our solution generates new IV's for each call and passes them as part of the url.

Ryan Heath
04/10/2012 11:56 AM by
Ryan Heath

We used a certain method to protect our image servers. We previously had build an url scheme that could specify width,height,dpi of uploaded images. Then we found out it was being abused and our servers were dying. The problem disappeared as soon as we had implemented this kind of protection. It was fun, yes :), but I would not use it for really sensitive information, though.

// Ryan

Gabriel Vince
04/11/2012 01:35 PM by
Gabriel Vince

Hi, indeed integrity is missing - a good example are authorized links for Amazon S3, it let you access content with known ID (uri), but there is expiration infromation (a unique nonce will do it too) and HMAC-SHA1 signed String containing a secret key, nonce and the ID.. But you are right, sometimes better than storing everything to server side session. And thank you - a very nice example for inspiration.

Comments have been closed on this topic.