An introduction to how Azure’s Shared Access Signatures let web apps share the load of downloading large files.
I recently found a subtle problem in a third-party Azure utility – it was giving access to rather more data than I had expected!
Shared Access Signatures
First a bit of background. Azure Storage has a powerful mechanism for securing access by end users to resources, called Shared Access Signatures (SAS). A common scenario is to use it to let end users directly download files from Azure Blob Storage, without having to go through an associated application.
Imagine that Alice and Bob are both using an Azure web application that stores large data files or videos. These files are private, so we don’t want Alice to be able to access Bob’s files and vice versa. We also don’t want anyone to be able to modify these files other than through the web application.
The simplest approach would be to require all access to go through the web application. The application would effectively act as a proxy, so when Alice or Bob wants to read a file, the application goes to Azure Storage, reads the contents of the file stored as a blob, and writes this back to the user. This does mean the web application has rather more work to do, but there would no direct access to the files.
Rather than requiring apps to do an extra layer of work shifting big files, Shared Access Signatures provide an intermediate level of access with some important limitations. A Shared Access Signature is effectively some additional information added to a resource’s URI in Azure Storage that lets us specify in a secure way how that resource is to be accessed.
To illustrate, I have created a container in Azure Blob Storage with the URL: https://alansdemo.blob.core.windows.net/blog-sample
This container is not publicly accessible (the default), so clicking on that link will give you a 404 error. In this container I created two files for Alice and Bob with the URLs https://alansdemo.blob.core.windows.net/blog-sample/File1.txt and https://alansdemo.blob.core.windows.net/blog-sample/File2.txt.
As the container isn’t public, these files can’t be accessed from those URLs. That’s good. To grant access we add a SAS component to the URLs – which looks like this:
The query string for the URL can be broken down as follows:
- st (signed start) – when we can start accessing the file using this link
- se (signed end) – when access using this link will end
- sp (signed permissions) – in this case “r” which means we are only granting read access
- sr (signed resource) – in this case “b” giving access to a blob
- sig (signature) – a digital signature (actually an HMAC) that allows the authenticity and integrity of the SAS to be checked.
Note that the signature is created using a key for the storage account in a one-way hashing operation that means that the signature is very easy to check but that it is practically impossible to recover the key from the signature.
So what happens if we sneakily change the filename to be another file in the same container – if we know there is a File1.txt we might guess there could be a File2.txt. A modified URL could look like this:
Fortunately, this doesn’t work – Azure checks the signature and realises I have changed something.
So what was the problem with the SAS that the application was creating?
It was creating a URL that looked like this:
and in this case if I change the filename the link is:
Oh dear – we can read the file through this “hacked” URL. Is this a problem with Azure Storage? Fortunately, the answer is a definite no! Looking closely, we can see that the application has used sr=c rather than sr=b – so the signed resource, the thing that we are giving access to, is the container rather than the individual blob. I’m happy to say that using a newer version of the utility fixed the problem!