Azure Blob Storage 由于授权标头而导致 403 认证失败

3

问题

我已经将一组图像(blob)上传到了私有的Azure Blob存储账户,但是当我尝试访问它们时,遇到了以下错误。


    GET https://<account-name>.blob.core.windows.net/<container-name>/<blob-name> 403 (Server failed 
    to authenticate the request. Make sure the value of Authorization header is formed correctly 
    including the signature.)

由于这是通过Django应用程序在服务器端完成的,因此我没有任何上传数据的问题。 我希望能够使用客户端JavaScript成功检索已上传的blob数据。

背景

我已经仔细阅读并实施了Microsoft Azure文档中有关通过使用共享密钥授权访问我的私有帐户的步骤。 这包括从构造我的签名字符串到使用HMAC SHA-256算法对该数据进行哈希处理的所有内容,如上述链接所述。

除了尝试调用Get Blob API端点的基于Vue的客户端接口以外,我将所有内容都运行在Docker容器上,如下所示。

最小可复现示例

引发此错误的代码如下:

// Add imports
const crypto = require('crypto');
const axios = require('axios');

// Set Azure blob storage data
const account = "<azure-blob-storage-private-account-name>"
const version = "2020-04-08"
const blob = "<blob-name>"
const container = "<container-name>"
const blob_uri = `https://${account}.blob.core.windows.net/${container}/${blob}`;
const today = new Date().toGMTString();
      
// Construct signature string
const CanonicalisedHeaders = `x-ms-date:${today}\nx-ms-version:${version}\n`;
const CanonicalisedResource = `/${account}/${container}/${blob}`;
const StringToSign = `GET\n\n\n\n\n\n\n\n\n\n\n\n` + CanonicalisedHeaders + CanonicalisedResource;

// Hash string using HMAC Sha-256 and encode to base64
const key = "<shared-access-key-in-base64>";
const utf8encoded = Buffer.from(key, 'base64').toString('utf8');
const signature = crypto.createHmac('sha256', utf8encoded).update(StringToSign).digest("base64");

// Construct the headers and invoke the API call
const blob_config = {
  headers: {
    "Authorization": `SharedKey ${account}:${signature}`,
    "x-ms-date": today,
    "x-ms-version": version
  }
}
await axios.get(blob_uri, blob_config)
  .then((data) => console.log(data))
  .catch((error) => console.log(error.message));

我尝试过的

我已经尝试了以下方法,但没有帮助我解决手头的问题。

  1. 更新CORS设置以避免出现与CORS相关的403禁止访问问题。 CORS设置
  2. 重新生成我的密钥和连接字符串。
  3. 检查我的本地机器和Docker容器上的DateTime设置,确保它们处于正确的GMT时间。
  4. 检查我的签名字符串的组成部分(规范化的标题、资源等)是否按照此处定义的规则构建。这里
  5. 阅读类似的StackOverflow和Azure论坛帖子,寻找解决方案。

这个“key”是从哪里来的?它是连接字符串中的“AccountKey”,还是为容器生成的SAS?无论使用哪个密钥,我都会收到相同的错误。我也尝试过来自SAS的“sig”密钥。 - Ahmad Karim
1
@AhmadKarim 我使用 key 来指代“存储账户密钥”,你可以按照这里的说明访问它:https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal。在这种情况下,任何一个你的存储账户密钥都应该可以使用。希望这能帮到你 :) - Valentin Stoykov
1个回答

3
请尝试通过更改以下代码行来解决问题:
const utf8encoded = Buffer.from(key, 'base64').toString('utf8');
const signature = crypto.createHmac('sha256', utf8encoded).update(StringToSign).digest("base64");

to

const keyBuffer = Buffer.from(key, 'base64');
const signature = crypto.createHmac('sha256', keyBuffer).update(StringToSign).digest("base64");

我认为您不需要将密钥缓冲区转换为UTF8编码的字符串。

另外几点:

  1. 考虑到您正在在浏览器中使用它,这存在巨大的安全风险,因为您正在向用户公开存储密钥。
  2. 您为什么要直接使用REST API而不是使用 Azure Storage Blob SDK
  3. 在基于浏览器的环境中,您应该使用基于 Shared Access Signature 的授权,而不是基于 Shared Access Key 的授权。

去掉.toString('utf8')后,403状态码错误得到了解决:我现在收到了数据!谢谢 :) 不过我有点惊讶,因为文档显示共享密钥应该从base64解码?关于第一点,我将把密钥存储在.env文件中。关于第二点,我之前不知道这个库:现在我会在我的应用程序中使用它!关于第三点,SAS的最佳实践是什么?SAS应该多久重新生成一次? - Valentin Stoykov
1
我有点惊讶,因为文档显示共享密钥应从base64解码。你正在这样做,但是然后你使用Buffer.from(key, 'base64').toString('utf8')将其转换为UTF8编码的字符串,这导致了问题。 - Gaurav Mantri
1
“2,之前我不知道这个库存在:现在我会在我的应用中使用它!” - 是的。SDK非常有用和方便,可以为您实现REST API。” - Gaurav Mantri
1
关于第三点,SAS 的最佳实践是什么?SAS 应该多久重新生成一次?- https://learn.microsoft.com/en-us/azure/storage/common/storage-sas-overview#best-practices-when-using-sas。希望对你有所帮助。 - Gaurav Mantri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接