-
Notifications
You must be signed in to change notification settings - Fork 653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Presigned URLs should use Query parameters for Additional request parameters (instead of headers) #2484
Comments
Totally agree in principle that a "presigned" URL that requires you to sideload headers is pretty nonsense. This API predates me and the context for it being that way is lost on me. I'll have to dive deeper on this. FWIW I do know that S3 as a service doesn't support every request parameter in a presigning context. Unclear if that fed into the design here or not, but it's come up before. |
Maybe a bit more context: I had an issue with with the expected bucket owner being passed as a query parameter in the aws-sdk-go (v1)[1]. Unfortunately, the real issue here seemed to be that the query parameter wasn't lowercased. I had a talk with the AWS support about this issue and during this process also got the response, that request parameters can either be provided as a header, or as a query parameter, but not both. If that is true for each parameter, I don't know tbh :( The S3 API documentation, to my knowledge, does not mention anything about that. And despite the fact, that I'm also not sure how to proceed here, if possible, it should probably be rechecked if all parameters can be passed as query parameters. And if so, an update to the documentation would be nice as well :D I'm also looking forward to your deep dive of why this is actually build like it is right now 👍 [1] which resulted in the previously send query parameter to be moved to a header in aws/aws-sdk-go@d05d03f |
@FlorianSW So, the request with the bucket-owner parameter in the query string may succeed, but I want to double-triple-confirm: does the expected ownership constraint actually apply to the request? e.g. if you create a presigned PUT for some object, and specify a non-matching expected owner, does that presigned request fail when the constraint is on the query?
As far as I can tell, this is in fact at play here. We appear to maintain a list of required signed headers which cannot be hoisted. Additionally, the addition of |
I forgot to mention this: I added an example (modifying generated code), to use the SDK v2 code to generate me a pre-signed URL with the expected bucket owner used in the query parameter, which you can find here: https://github.com/FlorianSW/aws-sdk-go-v2/tree/presigned/expectedbucketowner The one with the correct Account ID works as expected. The one with the wrong Account ID results in an AccessDenied error, as one would expect as well. One caveat as well: The query parameter needs to be passed with a lowercase key. E.g. if the parameter
which is currently pending investigation on why this happens, if I understood the AWS support correctly.
This update was at the same timeframe where the change in the v1 SDK was made: aws/aws-sdk-go#5062 |
Hi @FlorianSW , Thanks for all of the context and investigation. I do remember working on this not too long ago. We have discussed this as a team and I will have to do a deep dive to determine the best path forward. Thanks, |
Hi @FlorianSW, Thank you for your patience. I can confirm that lowercasing We already know that when Could you please provide us with the support ticket ID you have with S3? It will help us understand what has been done so far and allow the SDK team to share its findings with S3. |
Hi @RanVaknin, glad to see that we came to the same results :)
Sure, the Case ID is: 14086099621 |
Hi @FlorianSW , Thanks. It seems like that ticket was closed. I reopened it ( Thanks, |
Hi @FlorianSW, Thanks for your patience. I have created another Github issue which is meant to align the behavior of the SDK with the behavior of S3. (adjusting the casing of the expected bucket owner header) Now, this ticket calls out changing the behavior of the presigner to hoist headers into the query parameters instead of signing as headers. We cannot act on this as both can be (one or the other), and there are valid use cases of supplying headers in the presigned url as signed headers. Please refer to #2508 for tracking. Thanks, |
This issue is now closed. Comments on closed issues are hard for our team to see. |
Hi @RanVaknin, Sorry for my late reply. I looked for the case you linked above but was unable to find it in my account. I reached out to our TAM to assist in this, so that I can follow up with your open questions. I'll certainly look into the linked issue to see the progress of that, thanks. Best, |
To clarify (and circle back on what I said originally) it's now understood that the reason we have the headers "escape" hatch for presigning is for inputs that we know or at one point knew to not be supported by the S3 service in a query-hoisted context. In an ideal world, at some unspecified point in the future, S3 would recognize everything in the query and the returned header map would be vestigial. The exact set of headers that we don't hoist is manually maintained by each SDK (ours is here). You've correctly identified that our handling of the bucket owner parameter is wrong, so we're tracking that on its own in #2508. |
Describe the feature
AWS S3 allows additional request parameters to be set when a request is issued. In my case, I want to add the
expected bucket owner
to a GetObject request to S3. These parameters can either be added via a header or a query parameter.When using presigned URLs, their main intended use case is to encapsulate the entire request in the URL for later usage by a thrid-party who should not need to know additional information to add to the request (except executing the presigned URL).[1]
When creating a presigned URL in the aws-go-sdk-v3 with the following code (a GetObject request with an expected bucket owner parameter set):
the URL will look something like this:
https://<bucket-name>.s3.eu-west-1.amazonaws.com/<object-key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<access-key-id>%2F20240209%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240209T095920Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3Jp...&X-Amz-SignedHeaders=host%3Bx-amz-expected-bucket-owner&x-id=GetObject&X-Amz-Signature=e856...
A caller of this URL is required to add the
X-Amz-Expected-Bucket-Owner
header with the same value as provided by the creator of the pre-signed URL. This adds a burden and requires additional information that needs to be passed from the creator of the pre-signed URL to the executor of the URL. It also seems to be in contrast to the intended behaviour of pre-signed URLs, as they are now not entirely expressed as a URL anymore.When executing the above code to create a pre-signed URL, the URL should then look something like that, all parameters should be included as query parameters. This is supported by the S3 API:
https://<bucket-name>.s3.eu-west-1.amazonaws.com/<object-key>?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=<access-key-id>%2F20240209%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20240209T100408Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3Jp...&X-Amz-SignedHeaders=host&x-amz-expected-bucket-owner=<aws-account-id>&x-id=GetObject&X-Amz-Signature=7ef0789...
Executing this URL will work without needing to add any additional header values.
[1] Refer to "Using query parameters to authenticate requests is useful when you want to express a request entirely in a URL." (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)
Use Case
I have two components, which work with objects in a S3 bucket, one of them having actual aws credentials to access objects from buckets (the buckets are owned by different accounts). The service that has credentials for these buckets creates pre-signed URLs for the other component to use and work with these objects. As the buckets are configured by customers of these both services, I want to use the expected bucket owner as an additional safety net to ensure that both services are talking to buckets that are owned by the customer we intend them to be owned by. This should protect against a customer deleting a bucket and it being re-created by a malicious actor.
The service that creates pre-signed URLs is expected to pass a URL only to the other service. No other information is communicated at this step. This does, as of now, not work with presigned URLs and the aws-sdk-go-v2.
Proposed Solution
No response
Other Information
No response
Acknowledgements
AWS Go SDK V2 Module Versions Used
v1.24.1
Go version used
go version go1.21.6 darwin/arm64
The text was updated successfully, but these errors were encountered: