aws-sdk-ruby: S3 presigned urls do not sign headers x-amz-server-side-encryption-aws-kms-key-id, x-amz-server-side-encryption
There’s a regression of presigned urls that use KMS encryption. I’ve upgraded from 2.0.39 to 2.1.7, and 2.1.7 is broken.
In 2.0.39, when I call
Aws::S3::Object#presigned_url(:put, { server_side_encryption: 'aws:kms', ssekms_key_id: MY_KEY_ID })
I get a url with
...&X-Amz-SignedHeaders=host%3Bx-amz-server-side-encryption%3Bx-amz-server-side-encryption-aws-kms-key-id&...
But when I use 2.1.7, those headers are missing:
...&X-Amz-SignedHeaders=host&...
and when I apply the headers to my PUT request on that url (as required: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html#put-object-sse-specific-request-headers), I get:
<Error>
<Code>AccessDenied</Code>
<Message>There were headers present in the request which were not signed</Message>
<HeadersNotSigned>x-amz-server-side-encryption-aws-kms-key-id, x-amz-server-side-encryption</HeadersNotSigned>
...
</Error>
Rolling back to 2.0.39 fixes my problem.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Comments: 26 (12 by maintainers)
Commits related to this issue
- Tag release v2.1.31 References: #874, #880, #926, #939, #942, #944, #947, #956, #961, #962, #963, #966 — committed to aws/aws-sdk-ruby by awood45 9 years ago
@mcfiredrill Sorry to hear that you have to work around the problem by downgrading. So this is a known limitation from S3 side, we are actively working on a feature that allows flexible customizable presigned behavior in the long run. Meanwhile there is
aws-sigv4
available for customized signing and presigned requests.So here are some work-around with
aws-sigv4
This issue appears to have resurfaced in 2.3.4.
Results in following request URL:
Which results in the error:
Downgrading to 2.0.39 solves the issue.
I noticed in 2.0.39, the url has a semicolon after the X-Amz-SignedHeaders? Is the lack of the semicolon what is causing the error in 2.3.4?
Reopening - deprecating usage of Feature Requests backlog markdown file.
i’m still having problems with this in aws-sdk-core 2.10.47 / aws-sigv4 1.0.2.
i have a policy on my bucket that requires the x-amz-server-side-encryption parameter (i understand i can also just turn on encryption in the bucket settings, but that’s beside the point). i’m trying to create a presigned url for a PUT request (uploading a PDF file) like:
and then upload with an ajax request like this, using a file object from a browser form:
but i get a 403 when i try to execute the request. i see this in
Aws::Signers::V4#presigned_url
:so any param/header matching “x-amz-*” gets moved from the actual headers to the request params, and thus 1) isn’t present in the actual headers, and 2) isn’t included in the signed headers. this works fine if i remove that bucket policy, but with the policy in place, my understanding is that the server side encryption header must be included as an actual header and in the signed headers param. if i monkey patch the gem so it leaves the encryption header alone (i.e. comment out the snippet i pasted above) and add the header to my ajax request like this:
then it uploads successfully. so it seems like the code moving the headers to params is a bit overzealous and needs to have some exceptions for certain parameters.
edit: i see this issue and the comments about aws-sigv4, but i thought that had just been absorbed into the main aws-sdk-ruby gem now, so i wasn’t sure if this was still expected.
edit 2: using Aws::Sigv4::Signer directly as @cjyclaire described above does seem to work. this whole thing was pretty frustrating/confusing to figure out, though.
Here’s what I get using mostly your example code (different bucket, object key, credentials):
If I remove the bucket policy restriction described above, it works:
I need to have a bucket policy that restricts to aws:kms encrypted file uploads. So, is there a way to write a bucket policy that checks the URL parameters? If not, I need to use the headers.
So this change was made so that x-amz- headers would be sent via the querystring eliminating the need for users to provide these values twice. Before the change you would have to specify these values to the presigner and then again to your HTTP client to send them as headers. This was necessary because signature version 4 requires all header values that start with x-amz- to be signed, including their expected values.
When the change was made, it was intended to be a bug-fix so that a user could provide the values once to the presigner and have the URL querystring contain their values. This means the presigned URL no longer signs them as headers as they are part of the request URI.
Here is an example showing how to create a presigned PUT url with server-side-encryption and KMS and then upload something with Net::HTTP:
This means you should be able to simply remove those headers from your HTTP request and everything should work. Clearly this was an unintentional side-effect of the bug-fix, and a breaking change. The tricky part is if this is reverted then newer users would be broken (those counting on the values to be part of the URI and not currently sending them as headers). This is a sort of catch 22 and I’m not sure what the best path forward.
Thoughts?