aws-sdk-go-v2 icon indicating copy to clipboard operation
aws-sdk-go-v2 copied to clipboard

"SignatureDoesNotMatch" Error When Using PresignPutObject with Expires

Open xuanyu66 opened this issue 1 year ago • 4 comments

Describe the bug

When generating a presigned URL with PresignPutObject and setting the Expires field, attempting to put an object to S3 returns the error "SignatureDoesNotMatch". However, without setting the Expires field, it works fine.

Expected Behavior

return 200

Current Behavior

<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>

Reproduction Steps

// not working
request, err := s.s3Presign.PresignPutObject(ctx, &s3.PutObjectInput{
			Bucket:  &bucket,
			Key:     &key,
			Expires: &expire,
		}, s3.WithPresignExpires(6*time.Hour))
		if err != nil {
			return nil, err
		}


// works fine
		request, err := s.s3Presign.PresignPutObject(ctx, &s3.PutObjectInput{
			Bucket:  &bucket,
			Key:     &key,
		}, s3.WithPresignExpires(6*time.Hour))
		if err != nil {
			return nil, err
		}

Possible Solution

No response

Additional Information/Context

No response

AWS Go SDK V2 Module Versions Used

Compiler and Version used

go version go1.21.3 darwin/arm64

Operating System and version

macOS Ventura 13.6.4

xuanyu66 avatar Mar 01 '24 08:03 xuanyu66

Hi @xuanyu66,

Thanks for reaching out.

There is a distinction between the expires field you set on a putObject request which controls how long an object is cacheable, and the x-amz-expires query parameter set by SDK which controls the validity of a presigned URL.

For example, using the functional options will result in an x-amz-expires header, being hoisted into the query string.

	presignObject, err := presigner.PresignPutObject(context.TODO(), &s3.PutObjectInput{
		Bucket: aws.String("testbucket"),
		Key:    aws.String("sample.txt"),
		Body:   data,
	}, func(options *s3.PresignOptions) {
		options.Expires = 30 * time.Minute
	})
https://testbucket.s3.us-east-1.amazonaws.com/sample.txt?
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=REDACTED/20240302/us-east-1/s3/aws4_request&
X-Amz-Date=20240302T042717Z&
X-Amz-Expires=1800&
X-Amz-SignedHeaders=content-length;content-type;host&
x-id=PutObject&
X-Amz-Signature=REDACTED

When you set the expires request parameter on PutObject, the presigned request will calculate the signature as if an expires header is present at the time of making the request.

         presignObject, err := presigner.PresignPutObject(context.TODO(), &s3.PutObjectInput{
		Bucket:  aws.String("testbucket"),
		Key:     aws.String("sample.txt"),
		Body:    data,
		Expires: aws.Time(time.Now().Add(30 * time.Minute)),
	}, func(options *s3.PresignOptions) {
		options.Expires = 30 * time.Minute
	})
https://testbucket.s3.us-east-1.amazonaws.com/sample.txt?
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=REDACTED/20240302/us-east-1/s3/aws4_request&
X-Amz-Date=20240302T042932Z&
X-Amz-Expires=1800&
X-Amz-SignedHeaders=content-length;content-type;expires;host&
x-id=PutObject&
X-Amz-Signature=REDACTED

In the latter generated presigned URL you can see that expires is listed under the X-Amz-SignedHeaders parameter, which means that when you make a request to this presigned URL, youll have to add that header and the correct value to the request for the signature to match.

Thanks, Ran~

RanVaknin avatar Mar 04 '24 19:03 RanVaknin

So, I need to add a header such as "Expires: Sat, 27 Jun 2015 23:59:59 GMT"?

xuanyu66 avatar Mar 06 '24 02:03 xuanyu66

image @RanVakninI have already add this header, but the error still exists

And I also tried with the host header. image

xuanyu66 avatar Mar 06 '24 11:03 xuanyu66

@xuanyu66 The return of the PresignPutObject includes a set of http.Headers that need to be passed on the request for it to work.

This is not at all ideal, but is a necessity with S3 due to it not fully supporting query hoisting across various request parameters. We maintain a list of these inputs internally that we know (or knew in the past) to not work in the query, Expires is one of them.

lucix-aws avatar Mar 06 '24 15:03 lucix-aws