amazon-archives/aws-sdk-core-ruby

SignatureDoesNotMatch using Aws::S3::Object#presigned_url

Closed this issue · 3 comments

Hello, I am trying to do a very simple direct to S3 upload funcionality, but cannot get over 403 Forbidden response.

@new_s3_object = Aws::S3::Object.new S3_BUCKET.name, 'test22.jpg', client: S3_BUCKET.client
url = @new_s3_object.presigned_url :put, acl: 'public-read'

That url I hand over to jquery's fileupload and it fails returning 403 with this xml:

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId> ... </AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256 20141221T170842Z 20141221/eu-central-1/s3/aws4_request 541ba474d07a24d8a423a77292f45ef245246cf6c4bcf2bdb95366181a049509</StringToSign>
<SignatureProvided>4df9b85ee41319719dbe4a470df9f485a642521096b47298e4541ac8c9da7819</SignatureProvided>
<StringToSignBytes> ... </StringToSignBytes>
<CanonicalRequest>POST /test22.jpg X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential= ... %2F20141221%2Feu-central-1%2Fs3%2Faws4_request&amp;X-Amz-Date=20141221T170842Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=host&amp;x-amz-acl=public-read host:my-bucket.s3.eu-central-1.amazonaws.com host UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes> .. </CanonicalRequestBytes>
<RequestId>9F8E4357C44EA940</RequestId>
<HostId>HyUCoqgs9vfqzU+Oy+PR112JOrYCnxaLpBcm0hqCffpm60adFoOrM2g5EpcQe1L22EFjhef34iw=</HostId>
</Error>

The thing is, that this code on server-side goes successfuly (and the file appears in the bucket):

@new_s3_object.upload_file '/path-to-anythwhere/test22.jpg', acl: 'public-read'

That's why I am sure, that everything should be set up just right.
As far I've had the patience to dig into this library, I've concluded that both #upload_file and #presigned_url are using same internals for generating requisite headers/parametrs
So could you point me to what's wrong?

You are calling presigned_url with request type :put and then performing an HTTP POST request:

<CanonicalRequest>POST /test22.jpg X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential= ... %2F20141221%2Feu-central-1%2Fs3%2Faws4_request&amp;X-Amz-Date=20141221T170842Z&amp;X-Amz-Expires=900&amp;X-Amz-SignedHeaders=host&amp;x-amz-acl=public-read host:my-bucket.s3.eu-central-1.amazonaws.com host UNSIGNED-PAYLOAD</CanonicalRequest>

My understanding is that there is currently no support for generating a pre-signed POST URL. See #173.

@kabaka You are correct. I am not opposed to adding a pre-signed POST helper, but I would want to see it built around signature version 4. The v1 Ruby SDK uses the v2 S3 signature and will not work in any of the newest regions.

I'll leave here this gist I've made https://gist.github.com/doooby/2cb24eb2ea1180755c3c . It's my implementaion of simultaneous multiple images browser-based uploading to S3 using POST request with signature v4. It's build on top of this gem (aws-sdk v2.0.16.pre) and Rails. Maybe you could embed the DirectPostHelper class or at least use its code to add this feature.