2187-just_azure.svg

In this article, we’re going to talk about security and how to control access to containers and blobs using shared access signatures (SAS) and stored access policies. SAS’s are basically a URI with query parameters that specify options such as the expiration time, permissions, and a signed signature. I’ll cover these in detail later in this article. Note that shared access signatures can also be used with tables and queues, but I’m only going to discuss this in terms of blob storage.

What is SAS and why do I care?

The SAS (Special Air Service) regiment is the British Army’s most renowned special forces unit. Oh wait. Stupid search engine. Wrong SAS. Let’s try that again.

A shared access signature (SAS) is a URI that allows you to specify the time span and permissions allowed for access to a storage resource such as a blob or container. The time span and permissions can be derived from a stored access policy or specified in the URI. We’ll cover both of these in detail later.

If you think about it, SAS are similar to the British SAS in that they both provide security and protection against incursions by undesirables. Let’s take a closer look. At shared access signatures, I mean, not the British SAS. While the British SAS is interesting, I’m thinking I’m probably now being investigated by the NSA just for mentioning them.

Why would you want to use this feature? Because this allows the client to access the containers and blobs in your storage account without knowing your storage account keys. If you put your storage account keys in your client application, it could be hacked and your keys stolen and used by someone else to store their iTunes library in your storage account, replace your files with virus-infected copies, or steal your files and embarrass you publicly (similar to what happened in the 4th quarter of 2014 to a large company that rhymes with “stony”). With the storage account keys, there is no limit to the access to the storage account. Unlimited access probably isn’t something you want to share with everybody.

A primary example of a case where you might want to use an SAS is an application where users read and write their own blobs into your storage account. A company I worked at had a Software-as-a-Service web application that enabled the customer to upload blobs and store them in our company’s blob storage. Rather than putting the storage account keys in the web application, we could call a service to get an SAS that would give the customer a specific time interval, permission, and access to upload files to blob storage. For example, the customer could upload files for the next 10 minutes to a specific container. This also has the added advantage of reducing load on the application because you can get the SAS once every 10 minutes instead of calling it maybe hundreds of times in that time period.

Default Container permissions

When proceeding to control access to your blob storage, you start with the container permissions. You can set the permissions for each container in blob storage to one of three values:

  • Private – no public access to blobs or containers
  • Blob – public read access for blobs
  • Container – public read access for blob containers and blobs

If you set the permission to Private, then you can only access the blobs and containers if you have the storage account name and key, or you use a shared access signature.

If you set the permission to Blob, anybody with the URL to a blob in the container can read the blob and the blob properties and metadata. Unless they use a shared access signature with the appropriate permissions, they can not write the blob or retrieve any information about the container in which the blob resides, nor can they retrieve a list of blobs in the enclosing container.

If you set the permission to Container, the container and blobs are publicly readable. The blobs in the container can be listed and blobs can be downloaded; the container properties and metadata can be read; the blob properties and metadata can also be read. Note that even with this permission, you still can’t modify, upload, or delete blobs – you need the account key or an appropriate SAS.

This shows that you have some control over the granularity of the access to the blobs and containers using a combination of the permission on the containers and shared access signatures.

Stored Access Policies

There are two ways to create an SAS URI. First, you can create an ad hoc SAS to access a file or container and specify the expiration date and permissions to use. If you do this, you should consider setting the time span to something small such as 15 minutes in order to minimize the opportunity for someone else to use the same URL to access the same file. For example, if you are retrieving SAS URIs to show a set of pictures, someone could see the URIs in Fiddler and use them until they expire. The only other way to revoke access when using an SAS URI is to change the storage account key, which can have a severe impact, depending on how many applications are using that storage account. If you’re truly desperate, you can remove or move the blob(s)/container in question, but this can a pretty radical decision depending on whether those blob(s) or container are used by others.

The second way to create an SAS URI is to set up a stored access policy for the container and specify a name, start time, expiration time, permissions, etc. Then when you need an SAS URI, you can create it and specify the name of the stored access policy instead of all of the parameters required on the ad hoc version of the URI. The information will be retrieved from the stored access policy when authorization takes place. Also, unlike with the adhoc SAS URI, if you want to revoke access, you can simply change the stored access policy and all SAS URI’s that inherited from that stored access policy will immediately be modified; this is preferable to changing the storage account key!

A container can have up to 5 stored access policies. Each policy can be used by any number of shared access signatures. For example, you could set up different access policies for read/write and read-only access with different expiration times. There is no limit to the number of ad hoc URIs you can use.

When creating a stored access policy, you can specify any combination of Start Time, Expiration Time, and Permissions. If you specify them on the policy, then the parameters must be omitted from the actual SAS URI. Or you can specify some of these parameters in the stored access policy and the rest on the SAS URI. You actually could specify all of the parameters on the SAS URI instead of specifying them when creating the stored access policy, but if you do that, you can only use the stored access policy to revoke the signature, not to modify its behavior.

Together, the shared access signature and the stored access policy have to include all of the fields required to authenticate the signature, and can not have duplicates. For example, you can’t set up the stored access policy to have permissions R/W, and create the SAS URI and specify Read-only permission hoping it will override the policy (it won’t).

Stored access policies are set on a container by writing a complete list of the policies to be used. If you want to revoke access to one of the policies, you can remove it by writing the list of policies and excluding that one. If you want to change the permissions on one of the policies, you have to overwrite the stored policy list with a new list including the modified policy.

Now that you know about stored access policies, let’s look at the shared access signatures themselves.

Shared Access Signatures

What does an SAS look like? The SAS is a URI that includes all the information in its query parameters needed to authenticate access to the blob or container. The query parameters can include the following (the actual query parameter is in parentheses after the name of the parameter):

  • Blob URI: This is the address of the blob. You should always use HTTPS to construct a shared access signature. Example: https://myaccount.blob.core.windows.net/sascontainer/sasblob.txt Note that even if you use a stored access policy on a container to create the SAS for a blob, this is the address of the blob, not the container.
  • Storage services version (sv): This tells which version of the storage services to use. Example: sv=2014-02-14 This is optional, and will be set to the newest version available if not specified.
  • Start Time (st): This is the time the SAS becomes valid. If omitted, the SAS is effective immediately. Because of the time difference between computers (clock skew), you have to be careful if you use this. If you set a start time of right now, that could be 5 minutes in the future on another resource, and it may not allow access for another 5 minutes. Unless it’s well into the future, I recommend either leaving this setting off, or setting the start time to 15 minutes before the current time. This must be in ISO 8061 format. Example: st=2014-12-23T22%3A18%3A26Z
  • Expiration Time (se): This is the time after which the SAS is no longer valid. You should always use this, or associate it with a stored access policy that has this set. This must be in ISO 8061 format. Example: se=2014-12-23T22%3A23%3A26Z
  • Storage Resource (sr): This tells if the resource is a blob, queue, etc. Example: sr=b, which says the resource is a blob, or sr=c, which says the resource is a container.
  • Permissions (sp): This defines what operations can be performed against the storage resource. Example: sp=rw, which grants Read (r) and Write (w) access.
  • Signature (sig): This is used to authenticate access to the blob. This is an HMAC computed over a string-to-sign and key using the SHA256 algorithm, then encoded using Base64 encoding. (That sounds hard, doesn’t it? Fortunately, the storage client library has a call to create this for you, unless you just like to do things the hard way.)
    • Example: sig=Z%2FRHIX5Xcg0Mq2rqI3OlWTjEg2tYkboXr1P9ZUXDtkk%3D

Putting these together results in the following URI, which allows read/write access to a blob called sasblob.txt in sascontainer in the storage account myaccount from 12/23/2014 10:18:26 pm to 12/23/2014 10:23:26 p.m.

https://myaccount.blob.core.windows.net/sascontainer/sasblob.txt?sv=2014-02-14&st=2014-12-23T22%3A18%3A26Z&se=2014-12-23T22%3A23%3A26Z&sr=b&sp=rw&sig=Z%2FRHIX5Xcg0Mq2rqI3OlWTjEg2tYkboXr1P9ZUXDtkk%3D

This is an ad hoc SAS. You create this when you want to access a blob by specifying all of the necessary query parameters and the original URI of the blob.

Revocation

The important difference between ad hoc SAS URI’s and using a stored access policy has to do with revoking the permissions allowed (revocation). With an SAS, anyone who gets the URI can use it, no matter what process created it. So in the scenario mentioned earlier, if the customer captured one of the SAS URIs using Fiddler, he could send it to someone else who could then access the file in the storage account and upload it or download it.

An SAS URI is associated with the account key used to create the signature and the associated stored access policy (if any). If no stored access policy is specified, the only way to revoke a shared access signature is to change the storage account key.

In general, an SAS will work until:

  1. The SAS’s expiration time is reached.
  2. If you used a stored access policy, the SAS will stop working when the expiration time of the stored access policy is reached. This can happen because the time actually expired, or because the stored access policy’s expiration time was modified to be in the past.
  3. If you used a stored access policy, and the policy referenced by the SAS is deleted, the SAS would no longer work.
  4. The account key used to create the SAS is regenerated.

Creating an ad hoc SAS URI using the storage client library

Now that you have a complete understanding of shared access signatures, let’s see how to use the storage client library to get an SAS URI. I wrote a short method to return an ad hoc SAS URI. The CloudBlockBlob object is already set. How to set this is demonstrated in the third part of this series, Using the Storage Client Library.

This method gets the SAS URI for the blob. I’m passing in the blob, the SharedAccessBlobPermissions, and the number of minutes that I want the SAS to be valid. Note that for the SharedAccessStartTime, I am subtracting 15 minutes from the current time – you can also completely leave this parameter out and access will start immediately. This is because of the clock skew issue discussed earlier.

This creates a new Shared Access Blob, and sets the permissions, start time and expiration time, which is used to create the token with the query parameters. This is appended to the URI for the blob to give the SAS. Here is the value I get with the personal account info tokenized so you can’t upload your iTunes directory to my storage account:

https://[storageaccountname].blob.core.windows.net/[containername]/[blobname]?sv=2014-02-14&sr=b&sig=pJL%2FWyed41tptiwBM5ymYre4qF8wzrO05tS5MCjkutc%3D&st=2015-01-02T01%3A40%3A51Z&se=2015-01-02T02%3A00%3A51Z&sp=r

You can see the different query parameters as explained previously:

  • sv – storage version
  • sr – storage resource (blob in this case)
  • sig – signature (created by the storage client library using the account key)
  • st – start time
  • se – expiration time
  • sp – permissions (read only)

To set the permissions, I wrote a method to check several booleans passed in to a method (delete, write, list, and read). (In my case, I am getting these values from checkboxes on a screen.) Here’s the code:

If you want to generate the shared access signature on the container instead of on a blob, you simply call GetSharedAccessSignature on the container object instead of the blob object. The URI you pass in will be the URI of the container, and sr will be “c” instead of “b”.

Creating a stored access policy and using it to create an SAS URI

We talked earlier about the stored access policies. Let’s see how to create a stored access policy on a container, defining the constraints for any shared access signatures that use it. Then we will create an SAS URI for our blob using that stored access policy. You will see that in the new SAS URI, the query parameters are not all displayed any more, and that it will now show the signed identifier for the policy name.

First, let’s create the stored access policy on the container in blob storage. The reference to the cloudBlobContainer object has already been set when this is called. This is going to set the expiration time for 10 hours in the future, and set the permissions to read/write/list. Then it will clear the current list of policies and just add this one.

In the code above, I am creating a new collection of permissions. You could also retrieve the current list of permissions, search for a matching policy name and replace it, or add the new entry if it doesn’t already exist.

Now let’s write a method to call CreateStoredAccessPolicy and pass in a policy name, then get the token for the blob using this access policy instead of creating an ad hoc URI and specifying all of the values.

After calling this, the SAS URI returned is:

https://[storageaccountname].blob.core.windows.net/[containerName]/[blobName]?sv=2014-02-14&sr=b&si=TestPolicy&sig=o%2B5%2F0C%2BLm7tWWftNKvQEGKHlSt%2Bfs8No7FZkUk5T%2Bv0%3D

As expected, we have the si parameter for the stored access policy name, and the URL does not include the start and end times or permissions, because they are retrieved from the shared access policy upon authorization.

Best Practices

Always use HTTPS when creating or distributing an SAS. If you pass the SAS over HTTP, it can be read and used by someone performing a man-in-the-middle attack.

Use stored access policies where possible, because they allow you to revoke permissions without having to regenerate the storage account key. If you need perpetual access, set the expiration to be a long time, and make sure it is regularly updated to move it farther into the future.

When using ad hoc SAS URI’s, use an date range as small as possible to limit your exposure.

If necessary, have the client application renew the SAS. So if you have access to an image, and the SAS expires, the application should be able to renew it so the client doesn’t have a roadblock.

As noted before, be sure to account for clock skew by excluding the start time parameter or by subtracting 15 minutes from the current time when setting the SAS start time.

Be specific about the resource that needs access. If a customer only needs to access one blob, don’t give them access to the entire container.

Validate data written using an SAS after it is written to storage but before you use it, to make sure it is not corrupt or malicious.

Don’t always use SAS. If you want more control of the validation of the data going in, or you want to add additional security around authentication, you might want to create a middle-tier service to read/write to blob storage. Also, if there’s a simpler way to provide access, use it. For example, if you want all of the blobs in a container to be readable to the public, make the container public rather than creating an SAS for each client that needs access.

Use Storage Analytics to monitor your application. This will help you notice authentication failures due to a problem with your SAS provider service or the accidental removal of a stored access policy.

Summary

In this article, we discussed how you can use a combination of container permissions and shared access signatures to allow access to your blobs without having to provide your storage account name and key. We talked about creating ad hoc shared access signatures with all of the query parameters set, and how to use stored data policies to create default parameters that can be inherited.

The next article in this series is about moving data around – how to move blob data from one storage account to another, and how to use the Import/Export service and lots of tape.