AWS S3 Generating Signed Urls ''AccessDenied''

感情迁移 提交于 2020-01-01 06:13:12

问题


I am using NodeJs to upload files to AWS S3. I want the client to be able to download the files securely. So I am trying to generate signed URLs, that expire after one usage. My code looks like this:

Uploading

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) {
    // ...
})

Downloading

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

Issue

When opening the URL I get the following:

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>

I coultn't manage to find the mistake. I would really appreciate any help :)


回答1:


Your code is correct, double check the following things:

  1. Your bucket access policy.

  2. Your bucket permission via your API key.

  3. Your API key and secret.

  4. Your bucket name and key.

For bucket policy you can use the following:

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

Change bucket with your bucket name.

For users and access key permission (#2), you should follow these steps:

1-Goto AWS Identity and Access Management (IAM) and click on Policies link and click on "Create policy" button.

2-Select the JSON tab.

3-Enter the following statement, make sure change the bucket name and click on "review policy" button.

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

4-Enter a name for your policy and click on "Create policy" button.

5-Click on Users link, and find your current username (You already have the access key and secret for that)

6-Click on "add permission" button.

7-Add the policy we created in the previous step and save.

Finally, make sure your bucket not accessible from Public, add the correct content type to your file and set signatureVersion: 'v4'

The final code should be like this, thanks @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,
})



回答2:


Your code looks good but I think you are missing the signatureVersion: 'v4' parameter while creating the s3bucket object. Please try the below updated code.

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,
})

For more about signatureVersion: 'v4' see the below links

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

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

You can also try out the below nodejs library that create presigned url

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




回答3:


I kept having a similar problem but mine were due to region settings. In our back end we had some configuration settings for the app.

One of which was "region": "us-west-2" so the presigned url was created with this region but when it was called on the front end the region was set to "us-west-1".

Changing it to be the same fixed the issue.



来源:https://stackoverflow.com/questions/52667434/aws-s3-generating-signed-urls-accessdenied

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!