'AWS S3 Generating Signed Urls ''AccessDenied''
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 :)
Solution 1:[1]
Highest upvoted answer here technically works but isn't practical since it's opening up the bucket to be public.
I had the same problem and it was due to the role that was used to generate the signed url. The role I was using had this:
- Effect: Allow
Action:
- "s3:ListObjects"
- "s3:GetObject"
- "s3:GetObjectVersion"
- "s3:PutObject"
Resource:
- "arn:aws:s3:::(bucket-name-here)"
But the bucket name alone wasn't enough, I had to add a wildcard on the end to designate access to whole bucket:
- Effect: Allow
Action:
- "s3:ListObjects"
- "s3:GetObject"
- "s3:GetObjectVersion"
- "s3:PutObject"
Resource:
- "arn:aws:s3:::(bucket-name-here)/*"
Solution 2:[2]
I battled with this as well with an application using Serverless Framework.
My fix was adding S3 permissions to the IAM Role inside of the serverless.yml file.
I'm not exactly sure how s3 makes the presigned URL but it turns out they take your IAM role into account.
Adding all s3 actions did the trick. This is what the IAM role looks like for S3 ?
iamRoleStatements:
- Effect: Allow
Action:
- 's3:*'
Resource:
- 'arn:aws:s3:::${self:custom.imageBucket}/*'
Solution 3:[3]
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
Solution 4:[4]
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.
Solution 5:[5]
If your s3 files are encrypted than make sure that your policy also access to encryption key and related actions.
Solution 6:[6]
I had the same issue when i'm locally testing my lambda function its works but after deploy it didn't work. once i add the s3 full access to lambda function it worked.
Solution 7:[7]
I saw this problem recently when moving from a bucket that was created a while ago to one created recently.
It appears that v2 pre-signed links (for now) continue to work against older buckets while new buckets are mandated to use 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.
Our solution involved updating the AWS SDK to use this by default; I suspect newer versions probably already default this setting.
Solution 8:[8]
After banging my head for many hours with this same issue. I noticed that my account had a MFA setup , making the generation of the signed url with only the accessKeyId and secretAccesKey useless.
The solution was installing this https://github.com/broamski/aws-mfa
After running it , it asks to create a .aws/credentials file, where you must input your access id / secret and aws_mfa_device . The later will look something like
aws_mfa_device = arn:aws:iam::youruserid:mfa/youruser
The data can be found in your user in the aws console (Website)
After that you will find that credentials are populated with new keys with 1 week duration iirc.
Then simply generate a url again
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'
});
And this time it will work.
Remember to NOT pass any credentials to AWS.config , since they will be automatically picked from the .aws/credentials folder.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Spencer Sutton |
| Solution 2 | DylanA |
| Solution 3 | Vaisakh PS |
| Solution 4 | Ju66ernaut |
| Solution 5 | Arun |
| Solution 6 | ashen madusanka |
| Solution 7 | cwash |
| Solution 8 | mouchin777 |
