Common Encryption Pitfalls

Part 3 - Rethinking AccessURL Security Design

This is the last part of a three part blog series about AccessURL –

  1. Password Security and Cookies
  2. Security issues with AccessURL original implementation
  3. Solving all the issues by simple design change

In the previous post we saw a few security issues in the way AceessURL generates the passphrase and the id for the share URL. In this post we’ll see a way where we can get more security using almost the same security concepts AccessURL relies on a bit differently.

Before we go to the actual solution there is another security issue we need to discuss. This issue is a bit more subtle and is actually the main cause for the previous issues we’ve seen. I missed this issue initially and it only became clear when I tried to think how AccessURL can improve their security.

Please note that though AccessURL did improve their security since I reported the following issues, it does not mean they follow the design changes or any of the solution suggested in this article. It is up to you to decide if you trust any third party you share information with.

Issue 4 – Safety in numbers (Part 3)

Considering the issues described in Pt. 2 there are two very similar issues where secure random values are necessary. First with the URL identifier and then with the encryption passphrase.
Having two values that Eve has to guess might seem like it’s making the implementation more secure, but that is actually not true.
A very known and similar attack is called Meet in the Middle (MITM). MITM attack applies when we have a week encryption mechanism and we think we can get a much better security by doubling the encryption process but in fact, we are not getting almost any security benefit.
The Wikipedia article gives us the example of DES which uses a weak 56-bit key.
Using two 56-bit keys and running DES twice might make you think you are getting the equivalent of using 112-bit key But an attacker can actually find the key by only running 2^57 attempts.

If we go back to AccessURL link lets say we decide 16 chars is the maximum limit we are allowing for the random parts to be. With base 32 encoding (5 bits per char) we will have 80 bits of entropy. If we now have split it into two parts instead of getting  2^80 guesses as with 80-bit key. Eve only needs to run 2^40 guesses for the URL and another 2^40 for the passphrase which is only 2^41 actions which are easily breakable.

So can we have only one value but still have a passphrase that does not being sent to the server so all data is encrypted and only the user created it can decrypt it?
Yes, we can! And the best thing is we don’t really need to introduce any new concept to our logic.

Solving the MITM issue

To solve the Meet in the middle issue we need to have only one hard to guess value. As we described in the previous section we will use a value of 16 chars with ~80 bits of entropy. This does not mean 16 chars are secure enough only that using the same length passphrase with this implementation is much more secure than with the previous one.

Encryption Diagram

Encryption Diagram

As you can see in the diagram Alice asks for a Share URL.
A single 16 chars passphrase is generate.
The passphrase is hashed with 5000 rounds of PBKDF2 (= Key 1).
Key 1 is hashed with another 5000 rounds (= Key 2).
Key 1 is used to encrypt the data.
Key 2 and the encrypted data is sent to the server for storage.
The server should return the encrypted data to anyone who knows Key 2.
Alice receives a Share URL with the generated Passphrase in the hash part (so it will not be sent to the server)

Once Bob have the Share URL he can use it to get the original data –

Decryption Diagram

Decryption Diagram

Once the extension receives the link it can get the original data by –
Repeat the Hash process calculating Key 1 and Key 2 from the Passphrase.
Send Key 2 to the server in return for the encrypted data
Decrypt the data using Key 1

Notice that for the steps to be reproducible we have to use a fixed salt with the KDF. That is not an issue since the passphrase is randomly generated and the key space is large enough so we won’t have any collisions.

Since Key 2 is derived from Key 1 you might think it can be used to decrypt the ciphertext, but if you remember how Hash functions work it should be almost impossible to get Key 1 from Key 2.

If we consider the previous attack on the URL, guessing Key 2 and asking the server for the encrypted data, that is not going to work. Key 2, has 256 bits, it is such a vast opportunity space that it just can’t be guessed.

So let’s look at a more advanced attack. Assuming Eve now finds a security hole in our severs. she manages to download the entire database. This is by no way something trivial but it happened to some of the biggest players out there like LinkedIn Yahoo and The US Government.
Now Eve has full access to all the Key 2 values and their associated encrypted data.

How hard is it for Eve to break the encryption?
Guessing Key 1 is going to be hard (as with Key 2 that would be 256  bits which is just too much) but maybe Eve can try and guess the passphrase which is only 80 bits and then calculate Key 1 Key 2 and find the cookies it decrypts.
We’ll that is also going to be very hard.
Let’s assume Eve has some serious cracking gear. Remember the 400$ GPU we discussed before? Eve has 1000 GPUs each 1000 times faster than the 400$ GPU. This kind of hardware would probably cost at minimum a few million USD. Eve hardware can guess 3.4 trillion hashes for each millisecond!

Even with this crazy and expensive hardware, it will take here ~106,000 years to try the entire key space.
If we only used one round to calculate each KEY that would still take ~21 years.
Now you might think the previous solution would be almost as secure, but cracking 40 bits with this hardware assuming 10,000 rounds would take ~3 seconds.
So we are getting a much more secure solution for exactly the same cost.

Is it secure enough?

It is hard to say. I demonstrated that 40 bits are totally insecure and probably any solution that uses less than 70 bits will not be secure. After that it is hard to know it is common to assume that 128 bits are very secure but as we saw it all depends on how capable and determine our attacker is. 80 bits will probably be enough to stop 99% of potential attackers the other 1%  who might be capable of spending a few million bucks to attack the system will probably find a faster and cheaper way to get the data they want. But we should always use the strongest implementation we can. Using Base58 instead of Base32 We can add a few bits to the key space. Since it’s easy to get an array of 32-bit integers from crypto.getRandomValues() we might as well get 3 x 32 bits which when naively converted to Base58 will be maximum 18 chars long. We should use the longest passphrase our design allows. The only point where it wouldn’t make sense to increase the passphrase is when it contains 256 bits since at that point, it will be easier for the attacker to attack the Key directly without deriving it from the passphrase.

Solution summary

To sum things up here are the key features of a secure solution –

  1. Enable rate limiting on our API endpoints (patova, fail2ban)
  2. Use a secure encryption library (SJCL)
  3. Using CSPRNG generate random encryption key using one of two modes –
    1. Mode 1 Secure (default)
      URL hash  – 256 bits of randomly generated data encoded in Base58 (~48 chars)
    2. Mode 2 Easy to share (user has to explicitly opt-in on each URL share)
      URL hash  – 96 bits of randomly generated data encoded in Base58 (~18 chars)
  4. Encryption Key – Is derived from the URL hash using 5000 rounds of PBKDF2 and fixed salt
  5. ID – Is derived from the Encryption Key using 5000 rounds of PBKDF2 and fixed salt
  6. Encrypt all the shared data (including screenshots) and not just the cookie data.
  7. Send the encrypted data and the ID to the server

Notice that using a large number of rounds for deriving the keys doesn’t make a lot of sense when we are using a 256 bit key (since guessing the passphrase is as hard as guessing the key) so we can use just a single round of the KDF making the process faster when using the secure mode. But to keep the implementation a bit simpler we are using the same logic to both modes.