4

Imagine having images stored in a system with their 256 bit hash (BLAKE2b) as their unique ID. We want to produce a URL for each image, something like:

https://host/images/cleartext-image-ID

In order for one user not to be able to steal the URL for a particular image from another user, we encrypt the image ID using a symmetric key that is unique to each user:

ciphertext-image-ID-for-user = AES128-CBC(cleartext-image-ID, user-specific-key)

The URL then becomes:

https://host/images/ciphertext-image-ID-for-user

Because we need the ciphertext ID for a particular image to always be the same for the same user, we cannot use a random IV (Initialization Vector) for AES as the purpose of a random IV is the opposite: to ensure that the ciphertext will be different when encrypting the same message multiple times with the same key.

However, we believe this is not a problem since what we are encrypting is an ID that is a BLAKE2b hash which is indistinguishable from random. Therefore, no two IDs have anything in common – e.g., no common prefix or correlation for that sake.

If we absolutely need an IV, we could use the first 128 bits of the BLAKE2b hash (which is 256 bits) as the IV and then only encrypt the last 128 bits but we cannot see how this should increase the strength of the encryption. On the contrary, an adversary would then only have to decrypt 128 bits instead of 256 bits to get the cleartext ID. Also, the ciphertext will still be the same as we will always be using the same IV for the same image.

Is there something we have overlooked or is it correct that in this use case, we can safely omit the IV?

7
  • 3
    Have you considered using a signature, instead of encrypting the identifier? I.e. include and verify HMAC(cleartext-image-ID, user-specific-key)?
    – Sjoerd
    Commented Jun 10 at 14:21
  • 2
    @Sjoerd yes, but we need to be able to go from the ciphertext-image-id back to cleartext-image-id when decoding a URL and serving the correct image. With a HMAC we could not get to the original ID from the signature because that is a one-way function.
    – mgd
    Commented Jun 10 at 14:48
  • 1
    Your method requires the user-specific-key to be a) secret and b) the same for all image-URL. Because otherwise another user could reverse the URL to get the cleartext-image-ID and create their own URL with their own user-specific-key, thus accessing the image as another user. You could prevent this by adding a signature with a secret only known to the server. Commented Jun 10 at 15:05
  • @SteffenUllrich Yes, both a) and b) are required and we already fulfil that. The user-specific keys are secret and only known by the server. I am not sure I understand your suggestion?
    – mgd
    Commented Jun 10 at 15:13
  • 1
    If you feel adventurous, you could use a 256 bit block cipher such as SHACAL-2 or Threefish. This will give you a 256-bit permutation which is what you want, but these algorithms are less reputable than AES.
    – Sjoerd
    Commented Jun 11 at 8:21

2 Answers 2

6

This approach looks very awkward. The fact that you need strange workarounds like this “halfway-CBC” mode to make it work shows that there's definitely something wrong.

The solution to only allowing certain users to access certain files is access control. As soon as you've authenticated the user, you can freely decide what they can and cannot do. For example, for each image, you could store the corresponding owner in a database. If a user tries to view an image, the application checks whether they're the owner and then either displays the image or rejects the request. The huge benefit of this approach is that there's no need to keep the IDs secret, and you can adjust the permissions at any time. Even sharing becomes possible (should that ever become a useful feature).

Note that using the content hash as an ID means the ID leaks information about the content. More specifically, an attacker could check whether a certain file is present in your system. Unless you have very good reason for using this approach, you should use content-independent IDs. This can either be incremental numbers or purely random or partially random like UUIDv7.

Trying to use secret IDs as you've proposed has several major downsides.

  • You have to securely store and manages keys for all users.
  • When you rotate the user key, all URLs handed out to the user immediately become invalid. This can be a problem if the user has a lot of bookmarks or stored the URLs in some other way.
  • The entire encryption serves no useful purpose. You obviously have some authentication system already in place, because otherwise you wouldn't know which key to use when decrypting the IDs. So instead of checking access indirectly based on whether the decrypted ID points to any file, why not simply grant or reject file access based on the user identity?
  • Implementation encryption properly is not trivial. For example, AES-CBC does not authenticate the ciphertext. This can allow an attacker to decrypt the ciphertext through a padding oracle attack.
3
  • Comments have been moved to chat; please do not continue the discussion here. Before posting a comment below this one, please review the purposes of comments. Comments that do not request clarification or suggest improvements usually belong as an answer, on Information Security Meta, or in Information Security Chat. Comments continuing discussion may be removed.
    – schroeder
    Commented Jun 12 at 16:12
  • How does padding oracle apply here? Padding oracles apply when one sends ciphertext to the server and gets a response. Here the server encrypts and decrypts locally. How will one get the incorrect padding response from the server?
    – kelalaka
    Commented 2 days ago
  • @kelalaka: The server in this scenario accepts ciphertext from the client (encrypted image IDs), decrypts it and then sends a response. This is exactly where a padding oracle attack can happen. I'm not sure what you mean by "decrypt locally". This is a web application, so it communicates with remote clients.
    – Ja1024
    Commented 2 days ago
4

Yes, you can set the IV to a fixed value. Of course, using CBC does not provide many advantages, and it has some disadvantages.

CBC is vulnerable to bit flip attacks, where flipping a bit in the ciphertext results in a predictable flipped bit in the plaintext. This doesn't really have much impact on you, since your plaintexts are 256 bit opaque pseudorandom keys. One solution to this is to use authenticated encryption, for example by adding a HMAC.

You could use ECB mode, where the first and second block are encrypted separately. This would make it possible to swap blocks, but again this doesn't really impact you since you have pseudorandom plaintexts.

Using non-random encryption without integrity protection is probably sufficiently secure in practice. However, if a cryptographer sees it they will make complaining noises.

7
  • 2
    Regarding the comment on bit-flipping attack on CBC: As our plaintexts are opaque pseudo-random keys (from BLAKE2b) all plaintexts are completely uncorrelated. Therefore, flipping a bit in a known ciphertext (a user-specific image ID) and sending it to the system would not give you any better chance of guessing an image ID than providing a completely random 256 bit number as your guess. Do you agree?
    – mgd
    Commented Jun 11 at 7:58
  • @Sjoerd: You make it sound like cryptographic weaknesses are just some theoretical problem that doesn't matter in practice. As explained above, CBC without integrity protection is often vulnerable to padding-oracle attacks which make it possible to decrypt the entire ciphertext -- not just in theory but in practice. That's hardly a minor problem which can simply be ignored. When you have confidentiality without integrity, this can mean you lose confidentiality as well.
    – Ja1024
    Commented Jun 11 at 21:42
  • @Ja1024 as described the plaintext input is 256 bits which fits perfectly in two 128 bit block with no padding needed. Isn't padding the prerequisite for performing a padding-oracle attack?
    – mgd
    Commented Jun 11 at 22:20
  • 2
    @Ja1024 You have convinced me that you are right and I am wrong. Only disabling padding detection/removal on decryption or using an authenticated cipher will prevent a padding-oracle attack. (Fortunately, we had already disabled padding in both encryption and decryption.) Thank you for your patience.
    – mgd
    Commented Jun 13 at 10:07
  • 2
    @mgd: Thank you for taking this seriously and double-checking your configuration. The relevance of padding oracles is indeed not always obvious, but completely eliminating this attack vector is definitely worth it, I think.
    – Ja1024
    Commented Jun 13 at 14:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .