AWS S3 生成签名 Url ''AccessDenied''

AWS S3 Generating Signed Urls ''AccessDenied''

我正在使用 NodeJs 将文件上传到 AWS S3。我希望客户端能够安全地下载文件。所以我试图生成签名的 URLs,它在使用一次后过期。我的代码如下所示:

正在上传

const s3bucket = new AWS.S3({
    accessKeyId: 'my-access-key-id',
    secretAccessKey: 'my-secret-access-key',
    Bucket: 'my-bucket-name',
})
const uploadParams = {
    Body: file.data,
    Bucket: 'my-bucket-name',
    ContentType: file.mimetype,
    Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
    // ...
})

正在下载

const url = s3bucket.getSignedUrl('getObject', {
    Bucket: 'my-bucket-name',
    Key: 'file-key',
    Expires: 300,
})

问题

打开 URL 时,我得到以下信息:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
    <Code>AccessDenied</Code>
    <Message>
        There were headers present in the request which were not signed
    </Message>
    <HeadersNotSigned>host</HeadersNotSigned>
    <RequestId>D63C8ED4CD8F4E5F</RequestId>
    <HostId>
        9M0r2M3XkRU0JLn7cv5QN3S34G8mYZEy/v16c6JFRZSzDBa2UXaMLkHoyuN7YIt/LCPNnpQLmF4=
    </HostId>
</Error>

我没能找到错误。我真的很感激任何帮助:)

您的代码正确,请仔细检查以下事项:

  1. 您的存储桶访问策略。

  2. 您的存储桶权限通过您的 API 密钥。

  3. 您的 API 密钥和秘密。

  4. 您的存储桶名称和密钥。

对于存储桶策略,您可以使用以下内容:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::bucket/*"
        }
    ]
}

bucket 更改为您的存储桶名称。

对于用户和访问密钥权限 (#2),您应该按照以下步骤操作:

1-转到 AWS Identity and Access Management (IAM) 并单击策略 link 并单击 "Create policy" 按钮。

2-Select JSON 选项卡。

3-输入以下语句,确保更改存储桶名称并单击 "review policy" 按钮。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::YOURBUCKETNAME"
        }
    ]
}

4-为您的策略输入一个名称,然后单击 "Create policy" 按钮。

5-单击用户 link,然后找到您当前的用户名(您已经拥有该用户名的访问密钥和密码)

6-单击 "add permission" 按钮。

7-添加我们在上一步中创建的策略并保存。

最后,确保无法从 Public 访问您的存储桶,将正确的内容类型添加到您的文件并设置 signatureVersion: 'v4'

最终代码应该是这样的,感谢@Vaisakh PS:

const s3bucket = new AWS.S3({
    signatureVersion: 'v4',
    accessKeyId: 'my-access-key-id',
    secretAccessKey: 'my-secret-access-key',
    Bucket: 'my-bucket-name',
})
const uploadParams = {
    Body: file.data,
    Bucket: 'my-bucket-name',
    ContentType: file.mimetype,
    Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
    // ...
})
const url = s3bucket.getSignedUrl('getObject', {
    Bucket: 'my-bucket-name',
    Key: 'file-key',
    Expires: 300,
})

您的代码看起来不错,但我认为您在创建 s3bucket 对象时缺少 signatureVersion: 'v4' 参数。请尝试以下更新的代码。

const s3bucket = new AWS.S3({
    signatureVersion: 'v4',
    accessKeyId: 'my-access-key-id',
    secretAccessKey: 'my-secret-access-key',
    Bucket: 'my-bucket-name',
})
const uploadParams = {
    Body: file.data,
    Bucket: 'my-bucket-name',
    ContentType: file.mimetype,
    Key: `files/${file.name}`,
}
s3bucket.upload(uploadParams, function (err, data) {
    // ...
})
const url = s3bucket.getSignedUrl('getObject', {
    Bucket: 'my-bucket-name',
    Key: 'file-key',
    Expires: 300,
})

有关 signatureVersion: 'v4' 的更多信息,请参阅以下链接

https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html

您还可以尝试下面的 nodejs 库创建 presigned url

https://www.npmjs.com/package/aws-signature-v4

我一直有类似的问题,但我的是由于区域设置。 在我们的后端,我们有一些应用程序的配置设置。

其中一个是 "region": "us-west-2",所以预签名 url 是用这个区域创建的,但是当它在前端被调用时,区域被设置为 "us-west-1"

将其更改为相同解决了问题。

如果您的 s3 文件已加密,请确保您的策略也可以访问加密密钥和相关操作。

我在本地测试我的 lambda 函数时遇到了同样的问题,但在部署后它不起作用。一旦我添加了对 lambda 函数的 s3 完全访问权限 它起作用了

我最近在从不久前创建的存储桶移动到最近创建的存储桶时看到了这个问题。

看起来 v2 pre-signed 链接(目前)继续对旧存储桶起作用,而新存储桶必须使用 v4。

Revised Plan – Any new buckets created after June 24, 2020 will not support SigV2 signed requests, although existing buckets will continue to support SigV2 while we work with customers to move off this older request signing method.

Even though you can continue to use SigV2 on existing buckets, and in the subset of AWS regions that support SigV2, I encourage you to migrate to SigV4, gaining some important security and efficiency benefits in the process.

https://docs.amazonaws.cn/AmazonS3/latest/API/sigv4-query-string-auth.html#query-string-auth-v4-signing-example

我们的解决方案包括更新 AWS SDK 以默认使用它;我怀疑较新的版本可能已经默认了此设置。

https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-other.html#config-setting-aws-s3-usesignatureversion4

我也曾在使用无服务器框架的应用程序中遇到过这个问题。

我的修复是向 serverless.yml 文件中的 IAM 角色添加 S3 权限。

我不太确定 s3 是如何进行预签名的 URL 但事实证明他们考虑了您的 IAM 角色。

添加所有 s3 操作就可以了。这就是 S3 的 IAM 角色

iamRoleStatements:
  - Effect: Allow
      Action:
        - 's3:*'
      Resource:
        - 'arn:aws:s3:::${self:custom.imageBucket}/*'

这里最高赞的答案在技术上可行,但不实用,因为它打开了 public。

我遇到了同样的问题,这是由于用于生成已签名 url 的角色造成的。我使用的角色是这样的:

- Effect: Allow
  Action: 
    - "s3:ListObjects"
    - "s3:GetObject"
    - "s3:GetObjectVersion"
    - "s3:PutObject"
  Resource:
    - "arn:aws:s3:::(bucket-name-here)"

但是只有桶名是不够的,我不得不在末尾添加一个通配符来指定对整个桶的访问:

- Effect: Allow
  Action: 
    - "s3:ListObjects"
    - "s3:GetObject"
    - "s3:GetObjectVersion"
    - "s3:PutObject"
  Resource:
    - "arn:aws:s3:::(bucket-name-here)/*"

我为同样的问题苦思了好几个小时。 我注意到我的帐户有一个 MFA 设置,使得生成签名的 url 只有 accessKeyId 和 secretAccessKey 无用。

解决方案是安装这个 https://github.com/broamski/aws-mfa

在 运行 之后,它要求创建一个 .aws/credentials 文件,您必须在其中输入您的访问 ID / 密码和 aws_mfa_device。后者看起来像

aws_mfa_device = arn:aws:iam::youruserid:mfa/youruser

数据可以在aws控制台(网站)中找到你的用户

之后您会发现凭据中填充了新密钥,持续时间为 1 周 iirc。

然后再生成一个url

AWS.config.update({ region: 'xxx' });
var s3 = new AWS.S3();

var presignedGETURL = s3.getSignedUrl('putObject', {
    Bucket: 'xxx',
    Key: 'xxx', //filename
    Expires: xxx, //time to expire in seconds,
    ContentType: 'xxx'
});

这次会成功的。

切记不要将任何凭据传递给 AWS.config ,因为它们会自动从 .aws/credentials 文件夹中选取。