One of the more common Azure Storage shared access signature issues I see is “403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.” The challenge with this error is that it can often feel very random. When you run your code on some computers it works fine, but on other computers you get a 403. Or if you return a collection of SAS URLs to a client some of those URLs work fine and others get a 403.
The code to generate the SAS is typically very simple:
1: string sas = azureContainer.GetSharedAccessSignature (new SharedAccessPolicy ()
3: SharedAccessStartTime = DateTime.UtcNow,
4: SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
5: Permissions = SharedAccessPermissions.Write | SharedAccessPermissions.Read
So how can it behave so randomly?
As with most Azure Storage problems, we want to start with Fiddler. Download and install Fiddler and let it run and capture traffic while you reproduce the problem. The Raw results will look something like the following:
GET http://teststorage.blob.core.windows.net/Images/TestImage.png?st=2013-08-27T10%3A36%3A43Z&se=2013-08-27T11%3A31%3A43Z&sr=b&sp=r&sig=l95QElg18CEa55%2BXuhJIQz56gFFs%1FppYz%2E024uj3aYc%3D HTTP/1.1 Accept: image/png, image/svg+xml, image/*;q=0.8, */*;q=0.5 Accept-Language: en-US User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) Accept-Encoding: gzip, deflate, peerdist Proxy-Connection: Keep-Alive Host: teststorage.blob.core.windows.net X-P2P-PeerDist: Version=1.0
HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Proxy-Connection: Keep-Alive Connection: Keep-Alive Content-Length: 422 Via: 1.1 APS-PRXY-10 Date: Tue, 27 Aug 2013 10:36:41 GMT Content-Type: application/xml Server: Microsoft-HTTPAPI/2.0 x-ms-request-id: bf0d4d25-0110-4719-945c-afae8cbcdf0b AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:bf0d4d25-0110-4719-945c-afae8cbcdf0b Time:2013-08-27T10:36:42.3077388Z Signature not valid in the specified time frame
I have highlighted the key portions of the Fiddler trace. Notice the error message indicates that the shared access signature’s time frame is not valid.
Looking at the Request URL and comparing it to the SAS documentation we can see that the start time is set for 10:36:43. Given the code above we now that this is the value being returned on the client machine when calling DateTime.UtcNow.
Looking at the Response’s Date header we can see that the server side time is 10:36:41. 2 seconds earlier than the start time in the Request URL that the client created. This means that the client machine’s system time is at least 2 seconds faster than the system time for the front end authentication server handling that particular Azure Storage request. And the Azure Storage authentication server is going to reject this request because the client is attempting to access the storage resource 2 seconds earlier than the shared access signature allows. Clock drift like this is not an uncommon scenario.
So depending on which machine is generating the SAS URL, how recently the system time was synchronized, the time delta between the SAS-generating machine and the storage servers, and the speed of the client issuing the SAS request, you will randomly get HTTP 403 responses.
As we know from the SAS documentation the Start Time is optional (from MSDN: “Optional. The time at which the shared access signature becomes valid, in an ISO 8061 format. If omitted, start time for this call is assumed to be the time when the storage service receives the request.”). If you are going to specify DateTime.Now as your start time, then why specify a start time at all? The only reason you would want to specify a start time is if you were intentionally trying to time-delay a client’s access to a storage resource.
To resolve this issue simply remove the StartTime from the code such that it looks like this:
3: SharedAccessExpiryTime = DateTime.UtcNow.AddHours(1),
4: Permissions = SharedAccessPermissions.Write | SharedAccessPermissions.Read