aws / aws-sdk-go-v2

AWS SDK for the Go programming language.
https://aws.github.io/aws-sdk-go-v2/docs/
Apache License 2.0
2.59k stars 626 forks source link

CacheControl parameter in the PutObjectInput object is not working properly #2347

Closed jie closed 10 months ago

jie commented 10 months ago

Describe the bug

func (presigner Presigner) PutObject(
    bucketName string, objectKey string, lifetimeSecs int64, cacheControl string) (*v4.PresignedHTTPRequest, error) {
    request, err := presigner.PresignClient.PresignPutObject(context.TODO(), &s3.PutObjectInput{
        Bucket: aws.String(bucketName),
        Key:    aws.String(objectKey),
        CacheControl: aws.String(),
    }, func(opts *s3.PresignOptions) {
        opts.Expires = time.Duration(lifetimeSecs * int64(time.Second))
    })

    if err != nil {
        log.Printf("Couldn't get a presigned request to put %v:%v. Here's why: %v\n",
            bucketName, objectKey, err)
    }
    return request, err
}

Set the cachecontrol=max-age=60, but get:X-Amz-SignedHeaders=cache-control%3Bhost The return url looks like:

https://3.ap-southeast-1.amazonaws.com/public/image_upload/2023-11-03/image/fb3b6517-15cb-4bbc-bb13-868c7a6f0d95?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIAZF3Q7YNMIJ3FRD5P%2F20231103%2Fap-southeast-1%2Fs3%2Faws4_request
&X-Amz-Date=20231103T100831Z
&X-Amz-Expires=900
&X-Amz-SignedHeaders=cache-control%3Bhost
&x-id=PutObject
&X-Amz-Signature=cc1228d23ba07da5ec06bc83642ae46d13adb022dd525c7b76c6a49cab36ce9d

Expected Behavior

cachecontrol=max-age=60

Current Behavior

&X-Amz-SignedHeaders=cache-control%3Bhost

Reproduction Steps

run the PutObject method

Possible Solution

No response

Additional Information/Context

No response

AWS Go SDK V2 Module Versions Used

Compiler and Version used

v1.18.0

Operating System and version

MacOS 14

RanVaknin commented 10 months ago

HI @jie,

The current behavior you outlined is the expected behavior. The SDK doesn't add headers like Cache-Control (and others) directly as query parameters in the presigned URL. Instead, it signifies which headers will be required when the presigned URL is used, evident by the &X-Amz-SignedHeaders=cache-control%3Bhost in the URL.

When you, or anyone else with the presigned URL, wants to upload an object, the Cache-Control header needs to be set to max-age=60 in the HTTP request. The presigned URL "reserves" the right for you to set this header, and S3 expects and validates it based on the signature in the URL.

You can inspect the signing by enabling the signing logger on the presigner:

    input := &s3.PutObjectInput{
        Bucket:       aws.String(bucket),
        Key:          aws.String("hey.txt"),
        Body:         data,
        CacheControl: aws.String("max-age=60"),
    }
    signedRequest, err := presigner.PresignPutObject(context.TODO(), input, func(options *s3.PresignOptions) {
        options.ClientOptions = append(options.ClientOptions, func(op2 *s3.Options) {
            op2.ClientLogMode = aws.LogSigning | op2.ClientLogMode
        })
    })

In the following logs you can see the signed canonical string and that it does include the cache control key and value:

SDK 2023/11/04 20:55:19 DEBUG Request Signature:
---[ CANONICAL STRING  ]-----------------------------
PUT
/hey.txt
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=REDACTED%2F20231105%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231105T035519Z&X-Amz-Expires=900&X-Amz-SignedHeaders=cache-control%3Bcontent-length%3Bcontent-type%3Bhost&x-id=PutObject
cache-control:max-age=60
content-length:29036
content-type:application/octet-stream
host:REDACTED.s3.us-east-1.amazonaws.com

cache-control;content-length;content-type;host
UNSIGNED-PAYLOAD
---[ STRING TO SIGN ]--------------------------------
AWS4-HMAC-SHA256
20231105T035519Z
20231105/us-east-1/s3/aws4_request
b02cb080979e4520f9156ed70b699c67566b47dd481e7f0e471e671ae1REDACTED
---[ SIGNED URL ]------------------------------------
https://REDACTED.s3.us-east-1.amazonaws.com/hey.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=REDACTED%2F20231105%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231105T035519Z&X-Amz-Expires=900&X-Amz-SignedHeaders=cache-control%3Bcontent-length%3Bcontent-type%3Bhost&x-id=PutObject&X-Amz-Signature=REDACTED

In the case on my signed URL, the SDK signed content-length, content-type, host and cache-control. This means that when I use this presigned URL, my request must have those headers sent with the same values so that when the S3 service receives my request, and signs it itself, the signature calculated by the SDK client and the signature calculated by the S3 service would match.

Embedding headers like Cache-Control directly into the URL would risk exposing them to anyone who sees the URL. By keeping them as required signed headers, it ensures that the client making the request explicitly sets these headers, providing a clearer understanding of the request's intention and making certain that the headers aren't tampered with during transit.

I hope this clears things up. All the best, Ran~

jie commented 10 months ago

@RanVaknin You answered my question and solved my problem. Thank you!

github-actions[bot] commented 10 months ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.