In this article I will discuss how having just the ID of a Docker image can get you the contents of the image on all known Docker registry services, with the exception of Quay.io. I conclude with a fun game of capture the flag where you can try to steal a secret that I’ve placed in the various registries.
TL;DR You need to treat your Docker image IDs as secrets, like SSH keys or passwords. Do not post them in support tickets. No more screencasts where you run docker inspect. Do not post screenshots of your repository pages on index.docker.io. Just don’t do it.
Docker Images are like Onions
Why are IDs important to keep secret?
The answer stems from the way that most Docker repositories lay out and protect their images. If you look at the registry API specification, you can see that the spec treats all images as equal, whether they belong to you, or a stranger.
GET /v1/images/(image_id)/layer : get image layer for a given image_id
You’ll notice that there is nothing in that URL about who the user is, which repository it belongs to, or whether it is public or private.
Docker by design composes images out of layers, each of which represents a change to a base image. As a result, your repositories can and will share images with many other repositories. This flat image namespace and its associated sharing feature gives us vectors for maliciously gaining access to image layer data, some of which potentially contain secrets that you may have put in your “private” images.
Never Tell Me the Odds
At first, this doesn't seem so bad; After all, Image IDs are 256 bits of random data and that’s pretty hard to guess. As Wikipedia states:
50 supercomputers that could check a billion billion (1018) AES keys per second (if such a device could ever be made) would, in theory, require about 3×1051 years to exhaust the 256-bit key space.
Pretty difficult, right? Unfortunately, there are some considerations that significantly weaken the strength of of this seemingly uncrackable key. The first is that a shortened version is commonly passed around in docker. This shorter identifier is a 12 character hex string, which represents a decrease in entropy of 48 bits, reducing the computation time to 1.3x10^37 years. This is still forever in the practical sense. There are other mathematical and statistical phenomenon, such as the birthday problem, that show that a 256 bit key should still be unguessable from a practical sense.
A slightly more obscure attack on the key space may involve the usage of /dev/urandom for generating these “random” keys. If you run Docker on a desktop, which feeds environmental sources of randomness to the random number generator, you can be pretty sure that your keys are secure; the chances of someone else making the same mouse movements as you do is very unlikely. On the other hand, if you use Docker from a cloud server, which has no such sources of entropy, you may end up only getting pseudorandom numbers. An attacker who was sufficiently motivated may be able to sync up their machine with image IDs that you may expose publicly through the index, and guess your future “private” IDs. This, however, is still highly improbable.
The Social Problem
The real potential for attack, and the reason I say that you, the reader, must keep your image IDs private, is that they are not treated like secrets by Docker and many tools built on Docker.
Take a look at the following snippet of output from a docker inspect command against a running container:
Oops, there’s the entire 256 bit key. How about this screenshot from index.docker.io:
Ouch. It is also easy to imagine these IDs being thoughtlessly appended to support tickets, IRC channels, forums, etc. As usual, it’s the social aspect to security that proves to be the weakest.
There may also be technical problems where these IDs are inadvertently exposed. When a competitor to Quay.io recently launched, we immediately found that their implementation of the /tags endpoint from the Docker index specification didn’t check credentails and would give away complete image IDs to anyone who could guess the username and repository name! Since many users use the same username everywhere online, and many users pick “base”, “backend” or “test” as their first repository names, guessing these would not be hard at all, certainly less than the 3×1051 years of guessing that I was initially promised.
The Quay.io Solution
At Quay.io we put a layer of indirection between the image ID and the actual image storage that is specific to the repository to which it is associated. We still share images for performance reasons, but first you either have to prove to us that you have access to the image, or we make you re-upload it. This allows us to prevent these kinds of attacks completely while still maintaining the benefits of caching layers. Feel free to leak your Quay.io IDs as often as you want; we will never give your data away without appropriate access credentials.
Capture the Flag
If you would like to see what I’m talking about, I’ve put a secret, located at the path /secret, in an image and pushed it to a private repository in all of the following registries. If I missed any registries, please let me know and I will extend the challenge to those registries as well. I will post the first person who emails the correct secret to email@example.com below: