【问题标题】:POST s3 presigned url call in postman returns SignatureDoesNotMatch邮递员中的 POST s3 预签名 url 调用返回 SignatureDoesNotMatch
【发布时间】:2021-09-26 09:14:29
【问题描述】:

当调用从我的 node.js 服务器返回的预签名 POST 端点时,我收到了看起来像有效的响应:

注意我正在设置环境变量以使 POST 调用更容易重复。

当调用预签名 URL 时,我收到带有 SignatureDoesNotMatch 错误代码的 403 响应。我很难找到有关使用 POST 预签名 url 的资源。所以我的问题如下:

  1. 我是否正确调用了预签名 URL(见下文)?
  2. 如何调试才能了解这里发生了什么(我尝试打开存储桶日志,但没有记录任何内容)?
  3. 我可以进一步研究什么来解决这个问题?

在执行发布时,我的理解是调用是对返回的 url 进行 POST,并使用 form-data 包括正文中的所有“字段”,最终键被标记为文件并附加要上传的文件。因此,根据上述回复,我在邮递员中使用了以下调用: 标题:

Body(我尝试过包含和不包含 Content-Type):

但是,当我拨打此电话时,我收到 403 禁止响应和 SignatureDoesNotMatch 错误代码:

这是生成预签名 URL 的代码(使用“@aws-sdk/client-s3”:“^3.25.0”, “@aws-sdk/s3-presigned-post”:package.json 中的“^3.25.0”):

const { createPresignedPost } = require("@aws-sdk/s3-presigned-post");
const { S3Client } = require("@aws-sdk/client-s3");

const s3 = new S3Client({
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY_ID,
    secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  },
  signatureVersion: "v4",
  region: "eu-west-2",
});
async function getSignedUrl() {

  const params = {
    Bucket: "richbits-test",
    Key: "d3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f",
    Conditions: [["eq", "$Content-Type", "image/jpeg"]],
  };

  console.log(params);

  const signedUrl = await createPresignedPost(s3, params);

  return signedUrl;
}

我已经仔细检查了存储桶和密钥,但希望获得一些关于我如何前进的建议或任何可以帮助我更好地理解这些预签名网址的使用的有用资源。

【问题讨论】:

    标签: node.js amazon-s3 postman


    【解决方案1】:

    哇,AWS 关于这些标头的文档太糟糕了。这是我想出来的。

    1. createPresignedPost() 的输出是一个有两个键的对象:urlfieldsfields 对象包含提交帖子时必须使用的所有表单字段和相应的值。您可以将这些字段并且仅将这些字段复制到您的 Postman 请求中。
    2. AWS SDK 会自动添加一些字段,包括bucketkeypolicy,以及生成签名所需的几个字段。对于 JavaScript SDK,它们是 X-Amz-AlgorithmX-Amz-CredentialX-Amz-DateX-Amz-Signature(尽管对于 Python SDK,它们是 AWSAccessKeyIdsignature)。这些将在createPresignedPost()fields 键中返回,因此您无需手动生成它们。
    3. createPresignedPost()Fields 参数用于您要添加到表单的其他字段。 here 列出了可能添加的整个字段集。但是,SDK 会为您处理其中的许多,尤其是必需的。根据Python SDK docs,您可以在Fields 中包含的字段为aclCache-ControlContent-TypeContent-DispositionContent-EncodingExpiressuccess_action_redirect、@9876544 success_action_statusx-amz-meta-
    4. createPresignedPost()FieldsConditions 参数需要同步,以便Fields 中的任何字段在Conditions 中也有条件,反之亦然。

    总而言之,我认为您的getSignedUrl() 函数的问题在于它缺少createPresignedPost()Fields 参数,该参数具有Content-Type 的键值对(因为您有@ 987654360@)。如果Fields 参数中没有Content-Type,计算的签名将不包括Content-Type 字段。如果您随后遗漏了Content-Type 字段,您的签名可能匹配,但您的策略条件将失败,因为它要求Content-Type 存在并且等于image/jpeg。如果您包含Content-Type,那么您的签名将不匹配。

    这可能对你有用。

    const { createPresignedPost } = require("@aws-sdk/s3-presigned-post");
    const { S3Client } = require("@aws-sdk/client-s3");
    
    const s3 = new S3Client({
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      },
      signatureVersion: "v4",
      region: "eu-west-2",
    });
    async function getSignedUrl() {
    
      const params = {
        Bucket: "richbits-test",
        Key: "d3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f",
        Conditions: [["eq", "$Content-Type", "image/jpeg"]],
        Fields: {"Content-Type": "image/jpeg"},
      };
    
      console.log(params);
    
      const signedUrl = await createPresignedPost(s3, params);
    
      return signedUrl;
    }
    getSignedUrl().then(res => {
            console.log(res)
    })
    

    这个的输出应该是

    {
      Bucket: 'richbits-test',
      Key: 'd3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f',
      Conditions: [ [ 'eq', '$Content-Type', 'image/jpeg' ] ],
      Fields: { 'Content-Type': 'image/jpeg' }
    }
    {
      url: 'https://s3.eu-west-2.amazonaws.com/richbits-test',
      fields: {
        'Content-Type': 'image/jpeg',
        bucket: 'richbits-test',
        'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
        'X-Amz-Credential': '<YOUR_ACCESS_KEY_ID>/20211005/eu-west-2/s3/aws4_request',
        'X-Amz-Date': '20211005T111446Z',
        key: 'd3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f',
        Policy: '<SOME_BASE64_ENCODED_STRING>',
        'X-Amz-Signature': '<THE_SIGNATURE>'
      }
    }
    

    那么您的 POST 请求将包括表单字段 Content-TypebucketX-Amz-AlgorithmX-Amz-CredentialX-Amz-DatePolicyX-Amz-Signature。这是 HTML 表单的外观。

    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      </head>
      <body>
    
      <form action="https://s3.eu-west-2.amazonaws.com/richbits-test" method="post" enctype="multipart/form-data">
        Bucket:
        <input type="input" name="bucket" value="richbits-test" /><br />
        Key to upload: 
        <input type="input"  name="key" value="d3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f" /><br />
        Content-Type: 
        <input type="input"  name="Content-Type" value="image/jpeg" /><br />
        <input type="text"   name="X-Amz-Credential" value="<YOUR_ACCESS_KEY_ID>/20211005/eu-west-2/s3/aws4_request" />
        <input type="text"   name="X-Amz-Date" value="20211005T111446Z" />
    
        <input type="hidden" name="Policy" value="<SOME_BASE64_ENCODED_STRING>" />'
        <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
        <input type="hidden" name="X-Amz-Signature" value="<THE_SIGNATURE>" />
        
        File: 
        <input type="file"   name="file" /> <br />
        <!-- The elements after this will be ignored -->
        <input type="submit" name="submit" value="Upload to Amazon S3" />
      </form>
      
    </html>
    

    就调试这个问题而言,我没有从POST 对 S3 的请求中找到任何有用的信息。我要么没有得到回应,要么处于未经授权的状态,没有任何迹象表明问题出在哪里。

    【讨论】:

    • 非常感谢您抽出宝贵时间回复。我已经尝试了您建议的更改,但仍然得到相同的结果。我试过邮递员,也试过你提供的表格。在前者中,我仍然得到 SignatureDoesNotMatch 和后者 Access Denied 错误。我可以做更多的事情来回顾为什么这两次尝试之间存在差异,但现在我仍然对可能发生的事情一无所知。
    • @Richbits,当您收到 SignatureDoesNotMatch 错误时,您是否为 createPresignedPost() 添加了 Fields 参数,并且还在 Postman 的请求正文中包含了 Content-Type?您确定您的 Postman 环境变量工作正常吗?
    • 是的,我通过了这个: const params = { Bucket: "richbits-test", Key: "d3c0c9a0-ff91-11eb-bbe6-b9d90cd8bb8f", 条件: [["eq", "$Content-Type", "image/jpg"]], 字段: { "Content-Type": "image/jpg" }, };并在正文中包含内容类型。我还使用了您在上面发布的页面并设置了一个新用户并仔细检查了信誉。我现在不知道,我将尝试使用至少应该验证我正确使用凭据的 put。
    • @Richbits,我还尝试了使用 Postman 的请求,它使用您在图像中的设置工作。我确实注意到您的 Postman 请求将“Content-Type”字段设置为“image/jpeg”,并且您的评论说您的 params for createPresignedPost 包括 Fields: {"Content-Type": "image/jpg"}。我不确定这是否是一个错字,但这两个应该匹配;他们都应该是image/jpeg
    • 是的,我想我正在尝试使用 image/jpg,因为它与扩展名匹配,但我已经审查并知道它不是有效的 mime 类型。我从头开始查看,发现在 Postman 中 Policy 变量之后有一个空格,所以这可能解释了为什么它在那里不起作用。 HTML 表单也可以正常工作,所以我怀疑在我的原件中还有另一个错误的空间或类似的东西。感谢您的帮助,我会接受您的回答。
    猜你喜欢
    • 2019-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2021-10-13
    • 2015-09-05
    相关资源
    最近更新 更多