Using Container-Level Access Policies in Windows Azure Storage
Last week, I blogged about shared access signatures, one of the new blob storage features introduced in July. This feature lets you embed signatures in URLs to grant granular access to containers and blobs. In the approach I took in the blog post, you embed the access policy directly in the URL, which means there’s no way for you to modify or revoke permission after I’ve given out the URL. To limit the scope of these unrevokable privileges, explicit access policies in the URL are limited to granting permissions for up to only one hour.
To grant longer-term permissions or to retain the ability to modify or revoke permissions after handing them out, you can use another new feature called container-level access policies (MSDN documentation). These are named access policies that take the place of explicit policies in the URL. In this blog post, I’ll walk you through a simple example of using a container-level access policy.
Creating the Container-Level Access Policy
The first step is to create the container-level access policy. This is done using the “set container ACL” method (MSDN documentation). The following code from the sample creates an access policy that has an explicit start time, expiry time, and permission set.
var req = (HttpWebRequest)(WebRequest.Create( string.Format("http://{0}.blob.core.windows.net/{1}?restype=container&comp=acl", account, containername))); req.Method = "PUT"; req.Headers.Add("x-ms-version", "2009-07-17"); req.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R")); req.Headers.Add("x-ms-prop-publicaccess", "false"); var body = string.Format(@"<?xml version=""1.0"" encoding=""utf-8""?> <SignedIdentifiers> <SignedIdentifier> <Id>{0}</Id> <AccessPolicy> <Start>{1}</Start> <Expiry>{2}</Expiry> <Permission>{3}</Permission> </AccessPolicy> </SignedIdentifier> </SignedIdentifiers>", policyname, yesterday, tomorrow, permissions); req.ContentLength = body.Length; skc.SignRequest(req, new ResourceUriComponents(account, containername, "comp=acl")); using (var stream = new StreamWriter(req.GetRequestStream())) { stream.Write(body); stream.Close(); } req.GetResponse();
Building the Query String
Now that we have created an access policy, we need to construct a URL that makes use of it. The following code creates a web request that uses our new access policy. Note that because we’ve specified an explicit start time, expiry time, and permission set, we don’t specify them in the URL (as we did when we weren’t using a named access policy). This tripped me up at first. The start time, expiry time, and permissions are all optional when creating the container-level access policy, and any which are specified there should not be specified in the URL.
var sig = MakePolicySignature(account, key, containername, policyname); string text = null; using (var reader = new StreamReader( WebRequest.Create( string.Format("http://{0}.blob.core.windows.net/{1}/{2}?sr=c&si={3}&sig={4}", account, containername, blobname, policyname, Uri.EscapeDataString(sig) ) ).GetResponse().GetResponseStream())) { text = reader.ReadToEnd(); }
Signing the Request
In the previous code snippet, we used a method called MakePolicySignature to create the signature for our request. Here’s the source code for that method. See the shared access signature MSDN documentation for details on what should go in the “StringToSign.”
public static string MakePolicySignature(string account, string key, string container, string policyId) { string stringtosign = string.Format("\n\n\n/{0}/{1}\n{2}", account, container, policyId); using (var hmac = new HMACSHA256(Convert.FromBase64String(key))) { return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringtosign))); } }
When to Use Container-Level Access Policies
Shared access signatures give you the ability to create granular permission sets with or without the use of a container-level access policy, so when should you use them? Let’s take a look at some advantages and disadvantages to using container-level access policies.
Advantages:
- Container-level access policies let you grant permissions that last more than an hour.
- You can modify or revoke container-level access policies at any time.
Disadvantages:
- Container-level access policies are, as the name states, at the level of the container. You can’t use them to create permissions for a specific blob (except by putting it by itself in a container).
- There’s a limit of up to five access policies per container. This is a problem if you want to grant different permissions to many different users.
Because they give a greater level of control, I think container-level access policies will be my default way of granting permissions in URLs. I’ll forgo them for short-term access, like giving a user permission to upload a blob or embedding image tags on a web page.
Download the Sample Code
The above code snippets are part of a full command-line sample. Download the full source here.
Steve Marx works for Microsoft on Windows Azure.
I have a few questions, though.
1. Are these meant primarily for REST based access, direct browser downloads or both?
Assuming it's one of the latter two:
2. How will these interact with the new CDN feature? Does the CDN ignore the querystring and serve up anything it has cached for the path(very bad), can it evaluate the auth query params and decide on its own, or does it just pass everything through as though they were separate resources -- defeating the purpose of the CDN?
3. Have you considered adding client IP restrictions to the mix? For serving up video to SilverLight or Flash where you don't want people to embed the stream in their own players on other sites, being able to generate a token that combines the IP, path and optionally a timestamp for expiry and have the server check it is incredibly useful.
I could relay everything through a web role (I have a handler that proxies requests to the blob store and passes through important headers for ranges and whatnot) but this obviously wouldn't ever be usable with the CDN.