First of all, don’t use shared access key to access Azure Storage Account. It’s not recommended for security reason. Instead, you should use Azure AD based identity (user, service principal, etc.) and RBAC (resource/role based access control) where Azure Conditional Access policies can be applied to provide multi layer defense. Anyone from anywhere can access your storage account if access key is compromised and you allow All Network in storage account firewall.
Azure allows the use of access key but the responsibility to protect the resource is on the customers (in shared responsibility model). You should use storage account firewall to minimize the attack surface. The purpose of this post is to decipher the authorization header required to access private blob from storage account. Microsoft is fairly good on documentation and you can find the details at Authorize with Shared Key. We will do a PoC to access a blob which is private and you can’t access the blob unless you are authorized.
https://playground2020.blob.core.windows.net/container1/privateonly.txt
Will do a get request in Postman to read the blob but you will need to build the signature of the request and add the signature to Authorization header. Sample request-
GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Sun, 28 Jun 2020 17:17:07 GMT\nx-ms-version:2015-02-21\n/playground2020/container1/privateonly.txt
Sample authorization header based on the request string above-
Authorization: SharedKey playground2020:62/tf6uEmLqbsbAyJ8Wjbw8qiJg0VqOXV3fShKpTNz0=
You can handcraft the request but how do you get the hash based signature of it? Well, this is where you need to be innovative and write some codes or use tools! I wrote an Azure function to build the signature following the specification in Authorize with Shared Key
Don’t worry, that signature is no longer valid and you can’t use it to read private blob! Azure is good on helping developers and responded with -Request date header too old: ‘Sun, 28 Jun 2020 17:17:07 GMT’.
<?xml version="1.0" encoding="utf-8"?> <Error> <Code>AuthenticationFailed</Code> <Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:a4dd41be-001e-0005-127a-4d5b47000000 Time:2020-06-28T18:30:47.8345690Z</Message> <AuthenticationErrorDetail>Request date header too old: 'Sun, 28 Jun 2020 17:17:07 GMT'</AuthenticationErrorDetail> </Error>
What happens when you don’t specify correct date format in the header? I tried with a date: Sun, 28 Jun 2020 04:35:23 PM GMT. Azure responded with-
AuthenticationFailed
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:69bb737e-301e-0053-446f-4daaa8000000 Time:2020-06-28T17:14:08.8935852Z
The Date header in the request is incorrect.
What happens when the request is malformed? One example is, you don’t provide correct blob path. I didn’t know what to specify for the path and I tried the full path to the blob to build the signature.
GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Sun, 28 Jun 2020 17:17:07 GMT\nx-ms-version:2015-02-21\n/https://playground2020.blob.core.windows.net/container1/privateonly.txt
Guess what? Azure is so nice and told me what was wrong with my request.
AuthenticationFailed
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:d668b35d-301e-000e-7570-4da02c000000 Time:2020-06-28T17:18:29.6525871Z
The MAC signature found in the HTTP request 'frGnvGQA16vWpRcfFoV0lJK3jkpGsG8cwdSO6Ojv7CY=' is not the same as any computed signature. Server used following string to sign: 'GET
x-ms-date:Sun, 28 Jun 2020 17:17:07 GMT
x-ms-version:2015-02-21
/playground2020/container1/privateonly.txt'.
Bingo! I corrected the path to /playground2020/container1/privateonly.txt in the request and it worked.
Let’s do one final test with correct request and signature. We will use Postman to build the signature using Azure function and to request the blob with authorization header. We are going to blur the real key for security reason!
Don’t worry, by the time you are reading this blog post, the signature will no longer be valid. I don’t know exactly how long Azure honors the signature but it would be great if someone can share.
It would be disservice if I didn’t share the code behind building signature. I am C# guy, here we go! You can find it in my repo on GitHub. Also, here is how you pass the input parameters to Azure function. ** This Azure function does not log the request body (i.e. key) but it is advised that you should write your own code for building signature for production workload. **
{ "payload": { "stringtosign": "GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Sun, 28 Jun 2020 18:51:25 GMT\nx-ms-version:2015-02-21\n/playground2020/container1/privateonly.txt", "stringKey": "azure-storage-access-key-goes-here" } }
About the author: I am passionate about Information/Cloud Security and Cloud Compliance, always looking for opportunities to minimize security risks and to comply with regulations. For questions and comments, you can reach me over LinkedIn.