Tue, 11 Aug 2009

New Storage Feature: Shared Access Signatures

[UPDATE 5/2/2010] The code in this post is woefully out of date. See “Shared Access Signatures Are Easy These Days” for an updated sample.

WazDrop screenshotAmong other things, the latest storage release supports something called “shared access signatures.”  The basic idea is to let you create signatures that are more granular than the shared key for the whole storage account and then embed these signatures directly in a blob URL instead of an authorization header.

Making simple use of this new feature, I put together a sample called “WazDrop,” which operates similarly to applications like DropSend and YouSendIt.  It lets you upload a file and specify a duration it should be available.  It then returns you a special URL that can be used to access the uploaded file until the expiration time.

Play with the sample at http://wazdrop.cloudapp.net, and download the full source code.

Background

Windows Azure storage operates with “shared key authentication.”  In other words, there’s a password for your entire storage account, and anyone who has that password essentially owns the account.  It’s an all-or-nothing permission model.  That means you can never give out your shared key.  The only exception to the all-or-nothing rule is that blob containers can be marked public, in which case anyone may read what’s inside the container (but not write to it).

Shared Access Signatures

Shared access signatures give us a new mechanism for giving permission while retaining security.  With shared access signatures, my application can produce a URL with built-in permissions, including a time window in which the signature is valid.  It also allows for the creation of container-level access policies which can be referenced in signatures and then modified or revoked later.  This new functionality enables a number of interesting scenarios:

  • Giving blob read access only to logged-in users.  The web page can embed signed URLs for images with short expiration windows.
  • Creating an “incoming” folder which allows direct upload but no browsing or reading.  (This is the opposite of a public container.)
  • Give some users read-only access and some write-access based on their role.  Modify or revoke these privileges later as roles change.

Using the New Functionality

The StorageClient library in the SDK hasn’t yet been updated to make use of this new functionality, but it’s not very difficult to build your own code to produce simple shared access signatures.  For WazDrop, I wrote a simple method that produces a read-only signature for a blob:

public static string MakeBlobReadSignature(DateTime starttime, DateTime endtime,
    string account, string container, string blobname)
{
    string stringtosign =
        string.Format("r\n{0:yyyy-MM-ddThh:mm:ssZ}\n{1:yyyy-MM-ddTHH:mm:ssZ}\n/{2}/{3}/{4}\n",
        starttime, endtime, account, container, blobname);
    using (var hmac = new HMACSHA256(Convert.FromBase64String(
        StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration().Base64Key)))
    {
        return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringtosign)));
    }
}

The “r” at the beginning of the string means read-only permission.  You can also specify write, delete, and list (for containers).  The rest of the content of the string is the time range in which the permissions are valid and the resource we’re giving permissions for.

When constructing the URL, I use a set of query parameters that specify the kind of permission I believe I have along with the signature (computed in the above method) that proves I have those permissions.  Here’s the code where I construct the URL:

var uri = new UriBuilder();
uri.Scheme = "https";
uri.Host = string.Format("{0}.{1}", accountInfo.AccountName, container.BaseUri.Host);
uri.Path = string.Format("files/{0}", props.Name);
DateTime starttime = DateTime.UtcNow;
DateTime endtime = DateTime.UtcNow + TimeSpan.FromMinutes(minutes);
uri.Query = string.Format("st={0}&se={1}&sr=b&sp=r&sig={2}",
    Uri.EscapeDataString(starttime.ToString("yyyy-MM-ddTHH:mm:ssZ")),
    Uri.EscapeDataString(endtime.ToString("yyyy-MM-ddTHH:mm:ssZ")),
    Uri.EscapeDataString(MakeBlobReadSignature(starttime, endtime,
        accountInfo.AccountName, "files", props.Name)));


Those query parameters can look a bit intimidating at first, but they’re just shorthand.  “st” and “se” are the start and end times, “sr” is the kind of resource I have permissions for (“b” for blob), “sp” is the kind of permission (“r” for read), and “sig” is the signature from the above method.

Note that there are no headers involved (unlike the regular shared key authentication).  This means all the permissions are packaged up in the URL.  As the documentation points out, it’s important to note that the URL now contains sensitive data.  Be sure to use HTTPS (as my sample does) with these URLs to prevent eavesdroppers from gaining permissions you don’t want them to have.

Only Scratching the Surface

This sample only scratches the surface.  There’s a lot more to shared access signatures.  Probably the most interesting feature is the ability to create specific, named permission sets (“container-level access policies”), which means you can safely give out long-term permissions and simply revoke them or modify them later.  Check out the MSDN documentation (new features included later today) at http://msdn.microsoft.com/en-us/library/dd135733.aspx.

[UPDATE 8/23/2009] I’ve scratched the surface a bit more.  See my follow-up post about container-level access policies.

The Sample

Those links again: play with the sample at http://wazdrop.cloudapp.net, and download the full source code.  Enjoy, and let me know what you think!

[UPDATE 8/12/2009] That’s what I get for only testing my code in the middle of the night!  The original version of this sample didn’t work unless it was morning in the UTC timezone, because I was using “hh” instead of “HH” in the date format string.  The running sample, downloadable source code, and snippets below have all been updated with the fix.  A big thanks to Karthik from our storage team for pointing out the bug (and the correct fix)!

[UPDATE 1/27/2011] Fixed nomenclature… the feature is called “Shared Access Signatures”