简体   繁体   中英

Pre-Signed S3 URL Signature Does Not Match

Title says everything. Here is my code;

I'm using node-formidable for files.

form.on("end",function(field, file){
        params.Body = fs.createReadStream(params.filePath)
        delete params.filePath;
        S3.getSignedUrl('putObject',params, function(err, url) {
            if(err){console.log(err);}
            console.log(url);
        });
    })

After successful upload, url variable returns s3 url, something like this;

https://bucket-name.s3.amazonaws.com/746575308_8c081369df.jpg?AWSAccessKeyId=[key]&Expires=[date]&Signature=[signature]&x-amz-acl=public-read

But still getting SignatureDoesNotMatch error. In description says

The request signature we calculated does not match the signature you provided. Check your key and signing method.

Here is my parameters

params = {
    Bucket:"bucketname",
    Key: file.name,
    ACL: 'public-read'
}

What am i missing?

I ran into the same problem while using S3.getSignedUrl('putObject' , serverside, and then trying to use that url clientside.

What I noticed in my case, which might be relevant to yours, is that signatures created with all the S3.getSignedUrl take into account request headers. So if you are generating a URL, it will fail with the same error message you received unless sent with the same headers.

One example of a failure: Generated like this..

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000 };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });

The following request fails when a using that same url generated. This request was made from a browser.

RequestMethod: Put
Headers: {
    Accept:*/*
    Accept-Encoding:gzip, deflate, br
    Accept-Language:en-US,en;q=0.9
    Connection:keep-alive
    Content-Length:11768
    Content-Type:application/x-www-form-urlencoded; charset=UTF-8
}

And the difference is that the signature above created doesn't include content-type, where the request does specify a content-type. Params need to match headers, or the error thrown will be signature doesn't match.

Successful example below:

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000, Content-Type: 'application/x-www-form-urlencoded; charset=UTF-8' };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });

I ran into the same issue. The answer from farrellw explains the reason.

And in your case, you should add the header " x-amz-acl " with value " public-read " in your client request. Because you specified "ACL" with "public-read" when presigning on the server.

You can refer to https://aboutweb.dev/blog/signaturedoesnotmatch-s3-direct-upload/ for more details

Give this a try. You need to upload the object, then generate the signed url against an existing object.

var s3bucket = 'somebucket';
var s3Key = '/some/key',
var body = fs.createReadStream('/some/local/file.txt');

var params = {
    Bucket: s3bucket,
    Key: s3Key,
    Body: body
};
s3.upload(params, function(err) {
    if (err) {
        cb_1(err);
    } else {
        var params = {
            Bucket: s3bucket,
            Key: s3Key,
            Expires: parseInt(ttl)
        };
        s3.getSignedUrl('getObject', params, function(err, url) {
            if (err) {
                console.log(err);
            } else {
                console.log(err);
            }
        });
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM