Skip to content

MIGRATION ISSUE: v3 signed urls with meta data can't be used #7006

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

Open
4 of 5 tasks
camhart opened this issue Apr 10, 2025 · 4 comments
Open
4 of 5 tasks

MIGRATION ISSUE: v3 signed urls with meta data can't be used #7006

camhart opened this issue Apr 10, 2025 · 4 comments
Assignees
Labels
p2 This is a standard priority issue v2-v3-inconsistency Behavior has changed from v2 to v3, or feature is missing altogether

Comments

@camhart
Copy link

camhart commented Apr 10, 2025

Pre-Migration Checklist

Which JavaScript Runtime is this issue in?

Node.js (includes AWS Lambda)

AWS Lambda Usage

  • Yes, my application is running on AWS Lambda.
  • No, my application is not running on AWS Lambda.

Describe the Migration Issue

My backend uses s3 signed urls. Most of them work fine. However, signed URLs that use meta data can't actually be used. Whenever I attempt to use them I get a 403.

Code Comparison

v2

  const putParams = {
    Bucket: process.env.stage + '-myapp-' + process.env.version + '-images',
    Key: imageFileKey,
    ContentType: "image/jpg",
    ServerSideEncryption: "AES256",
    CacheControl: "max-age=86400, private",
    Metadata: {
      "accountId": accountId,
      "deviceName": deviceName,
      "identityId": identityId,
      "takenAt": imageRecord.takenAt.toString()
    },
    Expires: 24 * 60 * 60
  }

  const s3Url = s3.getSignedUrl('putObject', putParams)

v3

	const putParams = {
        Bucket: process.env.stage + '-myapp-' + process.env.version + '-images',
        Key: imageFileKey,
        ContentType: "image/jpg",
        ServerSideEncryption: "AES256",
        CacheControl: "max-age=86400, private",
        Metadata: {
            "accountId": accountId,
            "deviceName": deviceName,
            "identityId": identityId,
            "takenAt": imageRecord.takenAt.toString()
        }
    }

    // Create the command
    const putCommand = new PutObjectCommand(putParams);

    // Generate presigned URL (expires in 24 hours)
    const s3Url = await getSignedUrl(s3Client, putCommand, {
        expiresIn: 24 * 60 * 60,
		
		//I've tried both with and without unhoistableHeaders set
        unhoistableHeaders: new Set([
            "host", //I've tried with and without the host header
            "x-amz-meta-accountId", //I've tried lowercasing the headers and with camel case
            "x-amz-meta-deviceName",
            "x-amz-meta-identityId",
            "x-amz-meta-takenAt",
            "x-amz-server-side-encryption" //I've tried with and without this
        ])
    });

Observed Differences/Errors

When I attempt to use the signed URL from v2, I get a 200 and everything works. When I attempt to use the signed URL from v3, I get a 403.

	<?xml version="1.0" encoding="UTF-8"?>
	<Error>
		<Code>AccessDenied</Code>
		<Message>There were headers present in the request which were not signed</Message>
		<HeadersNotSigned>x-amz-meta-devicename, x-amz-meta-takenat, x-amz-meta-identityid, x-amz-meta-accountid</HeadersNotSigned>
		<RequestId>redacted</RequestId>
		<HostId>redacted</HostId>
	</Error>

Additional Context

Here's my client side code that works with v2 signed urls (but not with v3).

Request request = new Request.Builder()
                .url(path)
                .addHeader("Content-Type", "image/jpg")
                .addHeader("Accept", "text/html,application/json")
                .addHeader("x-amz-server-side-encryption", "AES256")
                .addHeader("Cache-Control", String.format("max-age=%d, private", 1 * 24 * 60 * 60)) //cache for 1 day

//I've updated the headers to be all lowercase instead of some camelcase and that doesn't make a difference.
                .addHeader("x-amz-meta-accountId", settings.getAccountId())
                .addHeader("x-amz-meta-deviceName", displayName)
                .addHeader("x-amz-meta-identityId", identityId)
                .addHeader("x-amz-meta-takenAt", Long.toString(takenAt))

                .put(body)
                .build();

        OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();
        Response response = null;

         response = client.newCall(request).execute();
         retResponse.status = response.code();
         retResponse.body = response.body().string();

With v2, it works. With v3, I get a 403.

@camhart camhart added needs-triage This issue or PR still needs to be triaged. v2-v3-inconsistency Behavior has changed from v2 to v3, or feature is missing altogether labels Apr 10, 2025
@camhart camhart changed the title MIGRATION ISSUE: [Your Title Here] MIGRATION ISSUE: v3 signed urls with meta data can't be used Apr 10, 2025
@zshzbh zshzbh self-assigned this Apr 11, 2025
@zshzbh zshzbh added p2 This is a standard priority issue investigating Issue is being investigated and/or work is in progress to resolve the issue. and removed needs-triage This issue or PR still needs to be triaged. labels Apr 11, 2025
@zshzbh
Copy link
Contributor

zshzbh commented Apr 11, 2025

Hey @camhart ,

Thanks for the feedback!

You need to change the headers to all lowercases, no camel case, all lowercase. For example -

"x-amz-meta-accountid"

If you command + click the unhoistableHeaders in your IDE, you can see the detailed definitions -
Image

I used to use x-amz-meta-accountId and got 403 response, and then after diving deep into the unhoistableHeaders definition, I changed it to x-amz-meta-accountid and got 200 response.

Let me know if that works for you!

Thanks!

@zshzbh zshzbh added response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days. and removed investigating Issue is being investigated and/or work is in progress to resolve the issue. labels Apr 11, 2025
@camhart
Copy link
Author

camhart commented Apr 12, 2025

Here's my updated backend call.

    const s3Url = await getSignedUrl(s3Client, putCommand, {
        expiresIn: 24 * 60 * 60,
        unhoistableHeaders: new Set([
            "host", //I've tried both with and without "host" header
            "x-amz-meta-accountid",
            "x-amz-meta-devicename",
            "x-amz-meta-identityid",
            "x-amz-meta-takenat",
            "x-amz-server-side-encryption"
        ])
    });

It's still a 403 but the error code has changed.

<?xml version="1.0" encoding="UTF-8"?>
<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>redacted</AWSAccessKeyId>
	<StringToSign>AWS4-HMAC-SHA256
20250412T010426Z
20250412/us-west-2/s3/aws4_request
redacted</StringToSign>
	<SignatureProvided>redacted</SignatureProvided>
	<StringToSignBytes>redacted</StringToSignBytes>
	<CanonicalRequest>PUT
/dev-myapp-images/35a86a11-4a60-4d29-8a8e-8beb22a1406c/v3/3699cd08-5b9a-483a-b9c6-5cd16c585796.jpg.thumbnail
X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&amp;X-Amz-Credential=redacted%2F20250412%2Fus-west-2%2Fs3%2Faws4_request&amp;X-Amz-Date=20250412T010426Z&amp;X-Amz-Expires=86400&amp;X-Amz-Security-Token=redacted&amp;X-Amz-SignedHeaders=host%3Bx-amz-meta-accountid%3Bx-amz-meta-devicename%3Bx-amz-meta-identityid%3Bx-amz-meta-takenat%3Bx-amz-server-side-encryption&amp;x-amz-checksum-crc32=AAAAAA%3D%3D&amp;x-amz-sdk-checksum-algorithm=CRC32&amp;x-id=PutObject
host:s3.us-west-2.amazonaws.com
x-amz-meta-accountid:me
x-amz-meta-devicename:deviceName
x-amz-meta-identityid:myIdentityId
x-amz-meta-takenat:1744247452141
x-amz-server-side-encryption:AES256

host;x-amz-meta-accountid;x-amz-meta-devicename;x-amz-meta-identityid;x-amz-meta-takenat;x-amz-server-side-encryption
UNSIGNED-PAYLOAD</CanonicalRequest>
	<CanonicalRequestBytes>redacted</CanonicalRequestBytes>
	<RequestId>7XJXKAYQ3DQNXP4R</RequestId>
	<HostId>+MgYvbgp6kKoROX3k5cJyo0cdDF4ahSyZHHye+sfEk9CEBifsxen75zAudrFm89JgbDyL1SDx3s=</HostId>
</Error>

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days. label Apr 13, 2025
@zshzbh
Copy link
Contributor

zshzbh commented Apr 14, 2025

Hey @camhart ,

What version of V3 JS SDK do you use? And what region are you at?

@zshzbh zshzbh added the response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days. label Apr 14, 2025
@camhart
Copy link
Author

camhart commented Apr 14, 2025

3.774.0

us-west-2

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days. label Apr 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p2 This is a standard priority issue v2-v3-inconsistency Behavior has changed from v2 to v3, or feature is missing altogether
Projects
None yet
Development

No branches or pull requests

2 participants