Developing Cloud Applications with Windows Azure Storage: Blobs
- 3/15/2013
- Blob basics
- Blob containers
- Blob addressing
- Business use cases
- Blob storage structure
- Navigating blob container hierarchies
- Storage Client library blob types
- Container and blob naming rules
- Performing create, read, update, and delete blob operations
- Shared Access Signatures and shared access policies
- Blob attributes and metadata
- Conditional operations
- Blob leases
- Using block blobs
- Using page blobs
- Blob snapshots
- Continuation tokens and blobs
- Conclusion
Shared Access Signatures and shared access policies
So what do you do when you want to grant to another party more granular control over blobs in a container without providing your private account key? This is where Shared Access Signatures are useful. A Shared Access Signature is a bit of cryptographic data that is appended to the query string of the URL that is used by Windows Azure storage to grant more granular control to blobs or their containers for a short period of time. After issued, an SAS is good for 60 minutes by default. This time can be extended by applying a Shared Access Policy to the Shared Access Signature, which will be introduced shortly; however, it is important to keep in mind that the shorter the duration the signature is valid, and the more minimal the authorization granted by the SAS, the stronger the effectiveness of the security the policy provides.
When you execute the following code to create a blob called test.txt in the blob container demo, and then attempt to access the contents of that blob using its address in your web browser, the attempt fails because the blob container is not public by default.
// Create a container (with no access) & upload a blob // Create a container (with no access) & upload a blob CloudBlobContainer container = account.CreateCloudBlobClient() .GetContainerReference("demo").EnsureExists(); container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Off }); CloudBlockBlob blob = container.GetBlockBlobReference("test.txt"); blob.UploadText("Data accessed!"); Process.Start("IExplore", blob.Uri.ToString()).WaitForExit(); // This fails
In the following sections, you will grant granular permission to the blob for a specified period of time.
Shared Access Signature
An SAS can be thought of as a security permission filter of your blob or blob container’s URI, with many segments providing the data necessary to establish the validity and duration of the signature provided in the sig field. The following is an example of what an SAS looks like.
http://account.blob.core.windows.net/container/blob ? st=2011-01-04T00:06:22Z & se=2011-01-04T01:06:22Z & sr=b & sp=r & si=Managers & sig=KKW...ldw=
The meaning of each segment of the SAS is described in Table 5-2.
Table 5-2 Shared Access Signature query string segments
Segment |
Query string segment purpose |
st |
Signed start time (optional) provided in UTC format (for example, 2011-01-04T00:06:22Z). |
se |
Expiry time provided in UTC format (for example, 2011-01-04T00:06:22Z). |
sr |
Resource (container’s blob or blobs). |
sp |
Permissions (r)ead/(w)rite/(d)elete/(l)ist. Permissions are supplied as single letters, and must be supplied in this order: rwdl. Any combination of these permissions is acceptable as long as they are supplied in the proper order (for example, rw, rd, rl, wd, wl, and dl). Specifying a permission designation more than once is not allowed. |
si |
Signature identifier (optional) relates an SAS to a container’s shared access policies. The signature identifier must be 64 characters or fewer. |
sig |
Shared Access Signature. The URL parameters are signed (HMACSHA256) with the account’s key. |
The permissions that may be applied to a blob container using the sp segment are provided in Table 5-3.
Table 5-3 Allowable shared access signature permissions
Permission |
Description |
Read |
Read content, properties, metadata, block list for blob (any blob in the container). |
Write |
Write content, properties, metadata, and block list for blob (any blob in container); copy blob is not supported. |
Delete |
Delete blob (any blob in the container). |
List |
Lists blobs in container. (This permission can be granted only to blob containers.) |
It is important to note that the generation of a Shared Access Signature is a client-side cryptographic activity that makes use of the storage account key. There are no network requests made of the storage service in order to create one. To create an SAS, you simply take all of the signed query string parameter values delimited by newline characters and then hash this value using the HMACSHA256 algorithm to create the sig parameter. Any request received by the storage service bearing an appropriately formatted and cryptographically intact signature will be granted the access defined in the sp (permissions) parameter.
Because the security signature and related parameters are provided in the URL, it’s very important to pay careful attention to the fact that they are subject to snooping and replay attacks if they are leaked to unintended parties, hijacked by someone sniffing traffic on the wire, or emailed to or from an employee who is ignorant of the security ramifications. You should use an SAS only in a production environment over HTTPS. You should also not use an SAS to expose blobs in a web browser, because the unexpired SAS will be cached in the browser history and hard drive of the client’s machine and could be used by an unauthorized party to perform data operations. Similar precautions should be taken to keep applications from storing the SAS in a persistent data store, or from exposing the SAS directly or inadvertently in its clear text form. If the SAS must be stored, you should consider encrypting it. If a user can see the SAS, it’s probably not secure enough and you may want to rethink your approach.
If you are using the Windows Azure SDK client libraries for the .NET Framework, the GetSharedAccessSignature method of a blob instance will ease your pain in manually constructing an SAS, saving you from having to fool around with a lot of messy URL string manipulations. The following code demonstrates how to create an SAS that is valid for a period of one hour beginning immediately, which will grant its bearer read, write, and delete permissions. You will see more on the SharedAccessPolicy and SharedAccessPermission classes shortly.
Creating a shared access policy
In the preceding code, you used an instance of a SharedAccessPolicy to create an SAS. The policy controls the usage characteristics of the SAS, for example, when it takes effect and how long it is valid. The policy also encapsulates, to the resource being protected, the permission set that you want to grant to the bearer of the SAS.
SharedAccessPermissions is an enumeration of bit flags that can be OR’d together to create the permission set necessary to meet your blob’s permission requirements. In the following code, you select a permission set comprising the desired combination of read, write, and delete permissions.
Permissions perms = SharedAccessPermissions.Read | SharedAccessPermissions.Write | SharedAccessPermissions.Delete
There is also a List permission for use with blob containers.
Applying a shared access policy to a blob container
To apply a shared access policy to a blob, you first must create an instance of SharedAccessPolicy and then set its permissions and the effective start and end time properties (given in coordinated universal time, or UTC), as shown in the next bit of code. You can then call the blob’s GetSharedAccessSignature method, passing the shared access policy object as an argument to retrieve a signed shared access URL that can be used by callers to perform subsequent CRUD operations granted on the blob (as specified in the shared access policy).
// Create an SAS for the blob var now = DateTime.UtcNow; SharedAccessPolicy sap = new SharedAccessPolicy { // Max=1 hr after start SharedAccessStartTime = now, SharedAccessExpiryTime = now.AddHours(1), Permissions = SharedAccessPermissions.Read | SharedAccessPermissions.Write | SharedAccessPermissions.Delete }; String sas = blob.GetSharedAccessSignature(sap); String sasUri = blob.Uri + sas; // This succeeds (in Internet Explorer, modify URL and show failure) Process.Start("IExplore", sasUri).WaitForExit();
Alternatively, you can protect the blob and other blobs in the container by adding the Shared Access Policy to the container’s collection of shared access policies.
Storing access policies
What if you have more stringent data access requirements than the SAS provides? For example, what if you require additional constraints on the starting or ending times that the SAS will be valid, or you require more granular control over the set of permissions being granted to a data storage item by an SAS? What if you require a means of revoking an SAS after it has been issued?
All of these tighter data access requirements can be met by augmenting a SharedAccessSignature with a stored access policy (SAP). Instead of being supplied as part of the query string parameters of the URL, the values that you select for your policy are stored with the data on the storage service. This decouples the SAS from the policy, thus allowing you to modify the parameters without having to re-issue another SAS. You can also revoke access granted to an SAS. The SAP is referenced by the signedidentifier field of the URL provided in Table 5-2. A signed identifier is a string containing 64 characters or fewer. You’ll see these classes shortly, but first it’s a good idea to review how to create a shared access policy through the RESTful API. In order to add new policies to the blob container, you must first retrieve the policies that are already present, as shown in the following HTTP GET request.
GET http://azureinsiders.blob.core.windows.net/demo ?restype=container&comp=acl&timeout=90 HTTP/1.1 x-ms-version: 2012-02-12 User-Agent: WA-Storage/2.0.0 x-ms-date: Tue, 18 Dec 2012 06:13:06 GMT Authorization: SharedKey azureinsiders:vB79RzKYOVkhdJ9NgMonq7OU4fI9DE40H0pxipiVQOQ= Host: azureinsiders.blob.core.windows.net Authorization: SharedKey azureinsiders:N6SZp4XX6NaC3ZOXHqVC94jTSnoUBQrgDV/By2+0HRU= Host: azureinsiders.blob.core.windows.net
This code returns the collection of policies in the body of the response. In this case, the SignedIdentifiers element is empty, showing that you have no stored access policies currently assigned to this blob storage container.
HTTP/1.1 200 OK Transfer-Encoding: chunked Content-Type: application/xml Last-Modified: Tue, 18 Dec 2012 06:09:29 GMT ETag: "0x8CFAAFD5FEF12F7" Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 x-ms-request-id: a34d3e7d-29a5-4742-8efa-7acab29aff4c x-ms-version: 2012-02-12 Date: Tue, 18 Dec 2012 06:13:05 GMT 3E <?xml version="1.0" encoding="utf-8"?> <SignedIdentifiers /> 0
To add a policy, you execute an HTTP PUT request against the blob container’s URI with the comp=acl query string parameter set. The body of the request contains a payload of signed identifiers. These signed identifiers represent the policies to be applied on the Windows Azure storage service side of the network for the blob container that is specified as the target of the request, as shown in the next code. Notice the SignedIdentifier ID is Managers and the Permission element has a value of rw.
PUT http://azureinsiders.blob.core.windows.net/demo ?restype=container&comp=acl&timeout=90 HTTP/1.1 x-ms-version: 2012-02-12 User-Agent: WA-Storage/2.0.0 x-ms-date: Tue, 18 Dec 2012 06:28:02 GMT Authorization: SharedKey azureinsiders:Oya7S8vBgOqBTBaKU3AMSL8ljnpiE9XAJrLq7kD1HYA= Host: azureinsiders.blob.core.windows.net Content-Length: 232 <?xml version="1.0" encoding="utf-8"?> <SignedIdentifiers> <SignedIdentifier> <Id>Revokable-12/18/2012 6:28:00 AM</Id> <AccessPolicy> <Start /> <Expiry /> <Permission>rw</Permission> </AccessPolicy> </SignedIdentifier> </SignedIdentifiers>
Like the preceding example, the following code snippet creates a shared access policy—with the Windows Azure client library—that grants read and write permissions on a blob container that has a signature identifier of Managers. The signature identifier is allowed to be any string up to 64 characters in length.
// Alternatively, we can add the SAP policies to the container with a name: String signatureIdentifier = "Revokable-" + DateTime.UtcNow; var permissions = container.GetPermissions(); // NOTE: A container can have up to 5 SAP policies permissions.SharedAccessPolicies.Add(signatureIdentifier, new SharedAccessBlobPolicy { Permissions = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write }); container.SetPermissions(permissions);
The SharedAccessPolicy cannot specify what is already present in the signature identifier.
// This SharedAccessPolicy CAN'T specify what is already present in the Signature Identifier sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy { SharedAccessStartTime = start, SharedAccessExpiryTime = start.AddYears(10) }, signatureIdentifier); sasUri = blob.Uri + sas; Process.Start("IExplore", sasUri);
Revoking SAS permissions
After the SAS has served its useful purpose, it may be necessary or desirable (as a precautionary measure) to revoke the granted permissions. This is accomplished by simply removing the SignatureIdentifier from the collection and performing another HTTP PUT operation against the blob container’s URI. There is really no difference between adding or deleting a stored SAS policy because they are both accomplished in an identical fashion: by simply providing a complete list of the permissions.
PUT http://azureinsiders.blob.core.windows.net/demo ?restype=container&comp=acl&timeout=90 HTTP/1.1 x-ms-version: 2012-02-12 User-Agent: WA-Storage/2.0.0 x-ms-date: Tue, 18 Dec 2012 06:28:06 GMT Authorization: SharedKey azureinsiders:jwtmE3xzpCu7xo/TTqJVWCZVJeO19MnXwNoPQMYdbCI= Host: azureinsiders.blob.core.windows.net Content-Length: 62 <?xml version="1.0" encoding="utf-8"?> <SignedIdentifiers />
With the Windows Azure client library, the container’s signature identifier can be used to revoke the SAS permission as follows.
// We can now revoke access on the container: permissions.SharedAccessPolicies.Remove(signatureIdentifier); container.SetPermissions(permissions); Process.Start("IExplore", sasUri); // This fails now