Creating Azure blob storage shared access signatures using JavaScript

March 16, 2011

...

There are quite a lot of examples on the Internet on how to create a shared access signature for Azure storage. However, the examples are often in C# or pseudo-code.

A Shared Access Signature is basically what is called a message authentication code – MAC – that is used to grant a user access to a restricted resource, often for a certain period of time. (In other words, a MAC typically has a validity period). In the case of Azure, a so-called Hashed MAC is used – HMAC. This is achieved using standard cryptography functionality, namely the SHA-256 hash algorithm. By using standard cryptography functionality, creating such MACs is also available in other programming languages and platforms.

In this blog entry I will show how to create MACs for Azure blob storage using JavaScript.

The first thing we need, is a library with cryptography functions. I chose Crypto-JS:

<script type="text/javascript" src="http://crypto-js.googlecode.com/files/2.0.0-crypto-sha256.js"></script>
<script type="text/javascript" src="http://crypto-js.googlecode.com/files/2.0.0-hmac.min.js"></script>

Next, we create a function to generate the signature we need:

Date.prototype.toIso8061 = function() {
   var d = this;
   function p(i) { return ("0"  + i).slice(-2); }
   return "yyyy-MM-ddThh:mm:ssZ"
      .replace(/yyyy/, d.getUTCFullYear())
      .replace(/MM/, p(d.getUTCMonth()))
      .replace(/dd/, p(d.getUTCDay()))
      .replace(/hh/, p(d.getUTCHours()))
      .replace(/mm/, p(d.getUTCMinutes()))
      .replace(/ss/, p(d.getUTCSeconds()));
};
function generateSignature(base64EncodedSharedKey, startTime, endTime, account, container, blobName) {
   var stringToSign = "rn{0}n{1}n/{2}/{3}/{4}n"
      .replace(/{0}/, startTime.toIso8061())
      .replace(/{1}/, endTime.toIso8061())
      .replace(/{2}/, account)
      .replace(/{3}/, container)
      .replace(/{4}/, blobName);
   var accessKeyBytes = Crypto.util.base64ToBytes(base64EncodedSharedKey);
   return Crypto.util.bytesToBase64(Crypto.HMAC(Crypto.SHA256, stringToSign, accessKeyBytes, { asBytes: true }));
}

Then, we can construct a URL that we will use to request a resource from the blob store:

var start = new Date(); // Start of the validity period of the MAC
var end = new Date(start.getTime() + (1000 * 60 * 30)); // End of the validity period, half an hour from now
var signature = generateSignature(startTime, endTime, "myaccount", "foryoureyesonly", "liveorletdie.avi");
var queryString = "?st={0}&se={1}&sr=b&sp=r&sig={2}"
   .replace(/{0}/, encodeURIComponent(startTime.toIso8061()))
   .replace(/{1}/, encodeURIComponent(endTime.toIso8061()))
   .replace(/{2}/, encodeURIComponent(signature));
var url = "http://myaccount.blob.core.windows.net/foryoureyesonly/liveorletdie.avi" + queryString;

Security warning

Although possible to do, it is not always advisable to generate the signature in a browser because generating the signature requires access to the shared key. The shared key is highly sensitive data, it is often not advisable to trust the browser client with this information if it should not be disclosed to the browser user. There are, however, some use cases where this is OK. I am planning to blog about one of them later on. Also, the code above can easily be used in a server-side scenario, such as a solution based on node.js.


Profile picture

Written by Vidar Kongsli who is a software professional living in Oslo, Norway. Works as a consultant, system architect and developer at Bredvid. You should follow him on Twitter