Sharing Private BLOBs with the Windows Azure SDK for PHP

June 7, 2012 update: The Microsoft Windows Azure team has released a new Windows Azure SDK for PHP. This release is part of an effort to keep PHP client libraries up to date with new Windows Azure features and to make PHP a first-class citizen in Windows Azure. The latest client libraries are on GitHub: https://github.com/WindowsAzure/azure-sdk-for-php. While the SDK hosted on CodePlex will continue to work for the foreseeable future, it is strongly recommended that new PHP/Windows Azure application use the SDK hosted on GitHub.

The work done by Maarten Balliauw and other contributors in building the SDK hosted on CodePlex was critical in unifying the PHP developer experience for Windows Azure. The Windows Azure team is grateful to these contributors for their pioneering work and looks forward to their continued support (and yours!) in adding to the new SDK on GitHub.

Thanks,

      
The Windows Azure Team


In the last couple of weeks, I’ve been spending quite a bit of time learning more about Windows Azure Blob Storage. Two questions I wanted to answers to were “How do I share private BLOBs with others?’ and “How do I access private BLOBs that others have shared with me?” The short answer to both questions is “Use shared access signatures”, though I’m not sure “shared access signature” is a phrase commonly used outside .NET circles. In this post I’ll answer both questions I’ve posed for myself and clear up any confusion around the phrase “shared access signature”.

Note: Since the Blob Storage API is a REST API (as it is for Azure Tables and Queues), the code in this post should run on any platform.

If you haven’t used the Windows Azure SDK for PHP to access Windows Azure Blob Storage, I suggest you start by reading this post: Accessing Windows Azure Blob Storage from PHP. Pay close attention to the Creating a Container and Setting the ACL section, as the rest of this post will assume that you have containers and BLOBs that are ‘private’.

What is a “shared access signature”?

A “shared access signature” is base 64-encoded Hash-based Message Authentication Code (HMAC) that is appended as a query string parameter to a URL. Using your storage account private key, you “sign” an agreed upon string (consisting of elements of the URL itself) and attach it as a query parameter to the URL. When a resource is requested with the signed URL, the resource provider reconstructs the agreed upon string from the URL and creates a signature with your private key (because the resource provider, Windows Azure in this case, knows your private key). If the signature matches the one provided with the URL, the resource request is granted, otherwise it is denied. If you have used Amazon Simple Storage Service (S3), you probably are familiar with signing URLs to make private S3 resources accessible (at least temporarily). A similar process is used for Windows Azure blobs.

An signed URL looks like this (obviously without the line breaks and spaces):

  1: https://bswanstorage.blob.core.windows.net/pics/Desert.jpg?
  2:      st=2011-11-08T20%3A03%3A35.0000000Z&
  3:      se=2011-11-08T20%3A53%3A35.0000000Z&
  4:      sr=b&
  5:      sp=r&
  6:      sig=R2G7CL1C0qOSfhMieEurr5THiVJYPVy7wj1OrA6120s%3D

That URL grants read access to the Desert.jpg blob in the pics container from 2011-11-08 at 20:03:35 to 2011-11-08 at 20:53:35. The shared access signature (R2G7CL1C0qOSfhMieEurr5THiVJYPVy7wj1OrA6120s%3D) was created by signing this string:

  1: r 2011-11-08T20:03:35.0000000Z 2011-11-08T20:53:35.0000000Z /bswanstorage/pics/Desert.jpg

For more detailed information about shared access signatures, see Creating a Shared Access Signature. (The code in that article is .NET, but it also contains interesting background information about blob access control.)

How to share private blobs and containers

Fortunately, the Windows Azure SDK for PHP makes the creation of shared access signatures very easy: The Microsoft_WindowsAzure_Storage_Blob class has a generateSharedAccessUrl method. The generateSharedAccessUrl method takes up to 7 parameters, which are…

  1. $containerName: the container name
  2. $blobName: the blob name
  3. $resource: the signed resource - container (c) - blob (b)
  4. $permissions: the signed permissions - read (r), write (w), delete (d) and list (l)
  5. $start: the time at which the Shared Access Signature becomes valid.
  6. $expiry: the time at which the Shared Access Signature becomes invalid.
  7. $identifier: the signed identifier

So, to create the example URL in the section above (which makes a blob readable for 50 minutes), this is what I did:

  1: require_once 'Microsoft/WindowsAzure/Storage/Blob.php';
  2: define("STORAGE_ACCOUNT_NAME", "storage_account_name");
  3: define("STORAGE_ACCOUNT_KEY", "storage_account_key");
  4:  
  5: $storageClient = new Microsoft_WindowsAzure_Storage_Blob('blob.core.windows.net', STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY);
  6:  
  7: $sharedAccessUrl = $storageClient->generateSharedAccessUrl(
  8:                      'pics',
  9:                      'Desert.jpg',
  10:                      'b', 
  11:                      'r',
  12:                      $storageClient ->isoDate(time()),
  13:                      $storageClient ->isoDate(time() + 3000)
  14:                     );

If you want to generate a URL that pertains to a container, leave the $blobName parameter empty. So, for example, this code produces a URL that makes the contents of the pics container listable:

  1: require_once 'Microsoft/WindowsAzure/Storage/Blob.php';
  2: define("STORAGE_ACCOUNT_NAME", "storage_account_name");
  3: define("STORAGE_ACCOUNT_KEY", "storage_account_key");
  4:  
  5: $storageClient = new Microsoft_WindowsAzure_Storage_Blob('blob.core.windows.net', STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY);
  6:  
  7: $sharedAccessUrl = $storageClient->generateSharedAccessUrl(
  8:                      'pics',
  9:                      '',
  10:                      'c', 
  11:                      'l',
  12:                      $storageClient ->isoDate(time()),
  13:                      $storageClient ->isoDate(time() + 3000)
  14:                     );

After generating these signed URLs, you can hand them to users so that they can temporarily access your blob storage resources. This, of course, begs the question, “How do I use one of these signed URLs?”

How to access shared private blobs and containers

In the case of a blob (as in the 1st example above), accessing it is a simple as pasting the signed URL into your browser. As long as the signature is valid (and the URL is used within the times specified), the blob in question should be accessible.

However, what about something like the second example above? In that example, the generated URL grants a user permission to list the contents of a container. Simply pasting the URL into a browser will not list the container contents, so how does one use the URL? The Windows Azure SDK provides a way for obtaining the permissions granted in a signed URL and using them.

To list the blobs in a container, I’ll have to use the listBlobs method on the Microsoft_WindowsAzure_Storage_Blob class, but to get permission to list the blobs, I’ll have to use the setCredentials method and the setPermissionSet method. Notice that all I need to do after calling the setCredentials method is pass the shared access URL to the setPermissionSet method, then I can call listBlobs:

  1: $storageClient = new Microsoft_WindowsAzure_Storage_Blob('blob.core.windows.net', 'account name', ''); 
  2: $storageClient->setCredentials( 
  3:     new Microsoft_WindowsAzure_Credentials_SharedAccessSignature() 
  4: );
  5: $storageClient->getCredentials()->setPermissionSet(array( $sharedAccessUrl ));
  6: $blobs = $storageClient->listBlobs("pics");
  7: foreach($blobs as $blob)
  8: {
  9:     echo $blob->name."<br/>";
  10: }

One more thing to note about the code above: In order to use the shared access URL to set permissions, notice that I had to know the name of the storage account in advance. This information is not contained in the signed URL and poses problems if you don’t know the storage account to which the signed URL is associated.

That’s it for today. If you are interested in how shared access signatures are generated, I suggest looking at the source code for the createSignature method on the SharedAccessSignature class in the Windows Azure SDK. (The crux is this…

  1: $signature = base64_encode(hash_hmac('sha256', $stringToSign, $this->_accountKey, true));

…but creating the string to sign is a bit tricky.)

Thanks.

-Brian

Share this on Twitter