Recommended pre-read
Code Sample
Scenario description
A website is offering files for download. These files are paid downloads: files can only be downloaded after entering valid credit card details. Also, once a file has been downloaded, the file cannot be downloaded again.

Figure 1 - Downloads application
When requesting a direct link to blob storage, which is also demonstrated in the Scenario - Offloading static content to blob storage, download is prohibited. Only if temporary access is requested the file can be downloaded. Whenever this temporary access expires, blob storage will disallow image download.

Figure 2 - Download not allowed after expiration of temporary access
This mechanism of temporary access is not to be implemented by the site owner. Instead, he can leverage the shared access signatures feature of Windows Azure blob storage.
Windows Azure storage operates with "shared key authentication", a password for the entire storage account. Anyone who has this account key can control the complete storage account. Due to this all-or-nothing permission model, it is advised not to share the shared key with anyone.
Shared access signatures give a mechanism for giving permissions while retaining security. With signed access signatures, an application can produce a URL with built-in permissions, including a time window in which the signature is valid.
A shared access signature looks like the following:
http://127.0.0.1:10000/devstoreaccount1/images/animals/1.jpg?st=2010-11-04T16%3A25%3A44.0000000Z&se=2010-11-04T16%3A26%3A04.0000000Z&sr=b&sp=r&sig=4yHJaeWBAHEsv%2FfjxsKgGBJuh6YOSsvFtwCxu1HJoFQ%3D
This is not meaningful to humans, but it is for blob storage. This URL represents the access type (read/write/…) for the storage type (container/blob) and for the given expiration window.
- Shared access signatures have different advantages:
- Allow download of files through blob storage for a limited amount of time.
- Provide write access to a blob container for a limited amount of time so someone can upload files between a specific time window.
- Enhance security to a storage account: granularly decide ACL settings on containers and blobs.
- …
Protecting downloads using blob storage and shared access signatures
In order to protect downloads using blob storage and shared access signatures, a shared access signature has to be generated. The Windows Azure SDK for PHP provides helper functions for making this an easy task.
Running the application
Listing downloads
When navigating to the web application, the main screen showing a list of downloads is presented. This page is served by a PHP script displaying a list of all available downloads. Blob storage will deny direct access to these downloads because the blob container has been marked private. In order to download the blob, the user should click the appropriate hyperlink for ordering the download and generating a shared access signature allowing temporary access.
![clip_image001[1]](/media/19384/Windows-Live-Writer_8c00ef157695_F66D_clip_image001%5B1%5D_3360fc97-c018-4c16-b58b-c76e2cd2e95b.png)
Figure 3 - Downloads application
Ordering a download
When the user clicks the Order file hyperlink on the main page, he is asked for valid credit card details. After clicking the Order button, the user is redirected to the requested download which is signed with a shared access signature and will be able to download it directly from blob storage. Note that when refreshing the download after a few seconds, direct downloading is no longer possible as the shared access signature has expired.

Figure 4 - Order download
Uploading content to blob storage
An extra screen has been developed for uploading static content to blob storage. This screen uploads contents of the uploads folder to Windows Azure blob storage and ensures the blob storage container is private (and blobs thus cannot be publicly downloaded through a web browser). This screen is similar to the technique demonstrated in the Scenario - Offloading static content to blob storage .

Figure 5 - Uploading static content to Windows Azure blob storage
Installing the sample scenario
The sample scenario demo application consists of the following files:
- scenario_downloadsecurity.php
- scenario_downloadsecurity_order.php
- scenario_downloadsecurity_transfer.php
- downloads folder containing available downloads
These can be deployed and used on any web server (Apache, IIS, …) and platform (Linux, Windows, …).
In order to run the samples out-of-the-box, ensure development storage is started on your Windows machine by clicking Start, All programs, Windows azure SDK, Development Storage. Refer to the Tutorial - Getting started for installing Windows Azure SDK for PHP and related components.
Running the sample in the Development Storage or in production with a Windows Azure Storage account
Throughout the scenario, development storage will be used for building examples unless specified differently. If you decided to work with a production Windows Azure storage account, use the production parameters for all constructors of the Microsoft_WindowsAzure_Storage_* objects. If the tutorial connects to the development blob storage service like this:
1: $storageClient = new Microsoft_WindowsAzure_Storage_Blob();
2://change that code into:
3:4: $storageClient = new Microsoft_WindowsAzure_Storage_Blob(
5:'blob.core.windows.net',
6:'<your azure account name, e.g. mystorageplayround',
7:'<your azure account key>' );
Coding the application
Listing downloads
When navigating to the web application, the main screen showing a list of downloads is presented. This page is served by a PHP script displaying a list of all available downloads. Blob storage will deny direct access to these downloads because the blob container has been marked private. In order to download the blob, the user should click the appropriate hyperlink for ordering the download and generating a shared access signature allowing temporary access.
The following is an extract of the HTML code displaying the main application screen:
1:<divclass="main">
2:<h2>Description</h2>
3:<p>This scenario demonstrates protecting downloads using blob storage
4: and shared access signatures.</p>
5:6:<ul>
7:<?php
8: // Fetchlistofblobs
9: $blobs = array();
10:11:try {
12: $blobs = $storageClient->listBlobs('downloads');
13: } catch (Exception $e) { }
14:15: // List blobs
16: foreach ($blobs as $blob) {
17: ?>
18:<li><?phpecho $blob->Name; ?> - $19.99 - <ahref="scenario_downloadsecurity_order.php?name=<?phpecho $blob->Name; ?>">Order file</a></li>
19:<?php
20: }
21: ?>
22:</ul>
23:</div>
Let's break this down. First, a list of blobs is retrieved from the Windows Azure blob storage container named downloads. This operation is wrapped in a try/catch structure to ensure that whenever the container named downloads is not available or any other error occurs, no error messages are displayed. Note that the $e variable contains all information about a possible error that occurred and should be logged and handled.
1: $blobs = array();
2:3: try {
4: $blobs = $storageClient->listBlobs('downloads');
5: } catch (Exception $e) { }
Next, the list of blobs is iterated and displayed in a simple HTML unordered list.
After the user clicks the Order file link, the user is redirected to the scenario_downloadsecurity_order.php script which will provide access to the download after entering valid credit card details.
Ordering a download
When the user wants to download a file, he is asked for valid credit card details. The following is an extract of the HTML code displaying the order screen:
1:<h2>Order file</h2>
2:<p>Enter your payment details below to order the file <i><?phpecho $_GET['name']; ?></i> for $19.99.</p>
3:4:<formmethod="POST">
5:<tableborder="0"cellspacing="0"cellpadding="2">
6:<tr>
7:<th>Credit card number:</th>
8:<td><inputtype="text"name="cc_number"value=""/></td>
9:</tr>
10:<tr>
11:<th>Expiration date:</th>
12:<td><inputtype="text"name="cc_expires"value=""/></td>
13:</tr>
14:<tr>
15:<th></th>
16:<td>
17:<inputtype="submit"name="action"value="Order"/>
18:</td>
19:</tr>
20:</table>
21:</form>
After clicking the Order button, the user is redirected to the requested download which is signed with a shared access signature and will be able to download it directly from blob storage. Note that when refreshing the download after a few seconds, direct downloading is no longer possible as the shared access signature has expired.
1:if (isset($_POST['action']) && $_POST['action'] == 'Order' && isset($_GET['name'])) {
2:// Grant temporary access to the blob
3: $sharedAccessUrl = $storageClient->generateSharedAccessUrl(
4:'downloads', // container
5: $_GET['name'], // blob
6:'b', // grant access to <b>lob
7:'r', // grant <r>ead access
8: $storageClient ->isoDate(time() - 5), // from 5 seconds ago
9: $storageClient ->isoDate(time() + 5) // to now + 5
10: );
11:12:// Redirect
13: header('Location: ' . $sharedAccessUrl);
14: }
The generateSharedAccessUrl method can be used for generating a shared access signature and expects the following parameters:
Parameter | Given value | Description |
$containerName | 'images' | The container on which the shared access signature is valid. |
$blobName | $_GET['name'] | The blob on which the shared access signature is valid. Note that this parameter is optional and can be given a null value when the shared access signature is valid for the complete container. |
$resource | 'b' | The resource that should be signed. Possible values are container (c) or blob (b). |
$permissions | 'r' | Permissions for the resource that should be signed. Possible values are read (r), write (w), delete (d) and list (l). |
$start | $storageClient->isoDate(time() - 5), | The time at which the shared access signature becomes valid. |
$expiry | $storageClient->isoDate(time() + 5), | The time at which the shared access signature expires. |
The access window for a download used in this scenario is 10 seconds. This window has been chosen to give the user a few seconds to select a location to save the download. After a few seconds, the file can no longer be downloaded using the signed URL and a new order should be placed.
Note that the total window has a maximum of 3600 seconds (1 hour). This means that shared access signatures with a longer validity period are not supported.
The generateSharedAccessUrl method generates a URL that represents the permissions granted and looks like the following:
http://127.0.0.1:10000/devstoreaccount1/downloads/download1.txt?st=2010-11-04T16%3A25%3A44.0000000Z&se=2010-11-04T16%3A26%3A04.0000000Z&sr=b&sp=r&sig=4yHJaeWBAHEsv%2FfjxsKgGBJuh6YOSsvFtwCxu1HJoFQ%3D
Uploading content to blob storage
The actual upload of static content to blob storage is done by a second screen in the application. This is not required: uploading files to blob storage can also be done using various Windows Azure blob storage clients available. By creating this functionality as a web page however, content can easily be uploaded just from a browser and optionally even be automated.
The sample code will be developed as a one-page script. As a side note, no input validation or sanity checks will be done, for the sake of simplicity.
At the top of this one-page script, the PHP code will be located. First of all, start with including and initializing the Microsoft_WindowsAzure_Storage_Blob class:
1:/** Microsoft_WindowsAzure_Storage_Blob */
2:require_once'Microsoft/WindowsAzure/Storage/Blob.php';
3:4:// Connect to blob
5: $storageClient = new Microsoft_WindowsAzure_Storage_Blob();
6:7:// Note that if you are working with production storage instead of development storage that the storage client will have to be initialized as follows:
8: $storageClient = new Microsoft_WindowsAzure_Storage_Blob(
9:'blob.core.windows.net',
10:'<your azure account name, e.g. mystorageplayround',
11:'<your azure account key>'
12: );
Next, two variables are defined:
1:// Settings
2: $foldersToUpload = array('downloads');
3:4:// Transfer log
5: $transferLog = array();
The $foldersToUpload variable contains the list of directories containing static content. The transferLog variable contains a log of all the performed uploads.
Once a user requests the current screen with a request variable of ?action=transfer, the actual upload of content to blob storage will be performed. For each folder defined in the $foldersToUpload variable, contents will be transferred. The following is the structure for that:
1:if (isset($_GET['action']) && $_GET['action'] == 'transfer') {
2:// Upload folders
3:4:foreach ($foldersToUpload as $folderToUpload) {
5:// ...
6: }
7: }
Within this structure, a blob container for each static folder is created on Windows Azure. Note that you can structure this differently if your application desires a different structure.
1:// Ensure container exists
2:if (!$storageClient->containerExists($folderToUpload)) {
3: $storageClient->createContainer($folderToUpload);
4: }
By default, a blob container is read/write for the Windows Account owner and access will be denied for any other request made to blob storage. To ensure that this is the case the container's ACL is explicity set to private, just in case someone has altered ACL settings before.
1:// Ensure blobs can be accessed by anyone
2: $storageClient->setContainerAcl($folderToUpload,
3: Microsoft_WindowsAzure_Storage_Blob::ACL_PRIVATE);
4:5:// Next, a list of all the files to upload is generated by calling the recursePath function.
6:7:// Generate list of files to upload
8: $filesToUpload = recursePath($folderToUpload);
9:10://The recursePath function is a recursive function getting the full path to all files within the folder to upload. Here's the code for that function:
11:function recursePath($path){
12: $files = array_diff(scandir($path), array('.', '..'));
13: $result = array();
14:15:foreach($files as $file) {
16:if (is_dir($path . DIRECTORY_SEPARATOR . $file)) {
17: $result = array_merge($result, recursePath($path . DIRECTORY_SEPARATOR . $file));
18: } else {
19: $result[] = $path . DIRECTORY_SEPARATOR . $file;
20: }
21: }
22:return $result;
23: }
The array that is generated by the recursePath function looks similar to this:
1:array(2) {
2: [0]=>string(20) "downloads\download1.txt"
3: [1]=>string(20) "downloads\download2.txt"
4: }
After the list of files is generated, the files will have to be uploaded to blob storage. Here's the code for that:
1:// Upload files
2:foreach ($filesToUpload as $fileToUpload) {
3: $targetBlobName = str_replace($folderToUpload . DIRECTORY_SEPARATOR, "", $fileToUpload);
4: $targetBlobName = str_replace(DIRECTORY_SEPARATOR, "/", $targetBlobName);
5: $storageClient->putBlob(
6: $folderToUpload,
7: $targetBlobName,
8: $fileToUpload);
9: }
Let's break down the parameters for the putBlob method:
Parameter | Given value | Description |
$containerName | $folderToUpload | A container is created per folder specified in the settings for this screen. |
$blobName | $targetBlobName | A stripped-down version of the local file name: the root path is removed and slashes are replaced with forward slashes. |
$localFileName | $fileToUpload | The local file that should be uploaded to blob storage. |
This is basically all it takes to upload a set of local files to blob storage.
