Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

S3RequestPresigner#presign causes SignatureDoesNotMatch error when S3 object key contains = #4862

Answered by yenfryherrerafeliz
sayyajp asked this question in Q&A
Discussion options

Checkboxes for prior research

Describe the bug

I tried to get presigned URL with custom parameter, so I implement below code.
But accessing downloadUrl generated by below method caused SignatureDoesNotMatch, when S3 objects' key contains "="(any other mark might cause also, but I didn't try).

SDK version number

@aws-sdk/client-s3: ^3.347.0 @aws-sdk/protocol-http: ^3.347.0 @aws-sdk/s3-request-presigner: ^3.347.0 @aws-sdk/util-format-url: ^3.347.0

Which JavaScript Runtime is this issue in?

Node.js

Details of the browser/Node.js/ReactNative version

node v18.15.0, google chrome v113.0.5672.126

Reproduction Steps

import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"
import { HttpRequest } from "@aws-sdk/protocol-http"
import { S3RequestPresigner, getSignedUrl } from "@aws-sdk/s3-request-presigner"
import { formatUrl } from "@aws-sdk/util-format-url"
async function getPresignedDownloadUrl(params: {
 bucket: string
 key: string
 customParam: string
 fileName?: string
 downloadUrlExpirationSec?: number
}): Promise<string> {
 const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${params.key}`
 )
 const presigner = new S3RequestPresigner({
 ...s3Client.config,
 })
 const httpRequest = new HttpRequest({
 query: {
 custom_param: params.customParam,
 "response-content-disposition":
 params.fileName != null
 ? `attachment; filename=${params.fileName}`
 : "attachment",
 },
 hostname: apiUrl.hostname,
 method: "GET",
 path: apiUrl.pathname,
 })
 const signedRequest = await presigner.presign(httpRequest, {
 expiresIn: params.downloadUrlExpirationSec ?? 60,
 })
 const downloadUrl = formatUrl(signedRequest)
 return downloadUrl
}
const url = await getPresignedUrl({
 bucket: "test_bucket",
 customParam: "customParamValue",
 key: "path/to/object", // Error occurs if "=" is included.
 fileName: "name_of_download_file",
 })
console.log(url)

Enter above url in the brower's address bar.

Observed Behavior

When entering presigned URL generated by above code in browser, SignatureDoesNotMatch occurs.

Expected Behavior

No SignatureDoesNotMatch occurs and can download properly.

Possible Solution

No response

Additional Information/Context

No response

You must be logged in to vote

Hi @sayyajp, sorry to hear about your issues. By reading the documentation about key naming guidelines, which can be found here, we can see that the symbol "=" is an special character and need special handling. Basically, in this case what you need to do is to URI-encode the key parameter. This is already handle by the getSignedUrl method, but since you are customizing your request then you need to do it yourself. Here is an example for how you can do it:
Instead of:

const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${params.key}`
)

You need to do:

const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${encodeURIComponent(params.key)}`
);

And that sho...

Replies: 3 comments 2 replies

Comment options

Hi @sayyajp, sorry to hear about your issues. By reading the documentation about key naming guidelines, which can be found here, we can see that the symbol "=" is an special character and need special handling. Basically, in this case what you need to do is to URI-encode the key parameter. This is already handle by the getSignedUrl method, but since you are customizing your request then you need to do it yourself. Here is an example for how you can do it:
Instead of:

const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${params.key}`
)

You need to do:

const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${encodeURIComponent(params.key)}`
);

And that should fix your issue.

I hope this helps!

Thanks!

You must be logged in to vote
1 reply
Comment options

I don't think this is correct. You'd still get signature mis-match for a certain characters like ( and ).

Avoid storing special characters on S3 as much as you can.

For those who can't, follow what AWS SDK for Java does - Note that encodeUriComponent and URLEncoder don't work the same way.

Answer selected by yenfryherrerafeliz
Comment options

I am going to convert this into a discussion so that people facing similar issue can find guidance regarding.

Thanks!

You must be logged in to vote
1 reply
Comment options

@yenfryherrerafeliz
With reference to the above, I could properly presign as below.
Thank you for replying.

const encodedKey = params.key.split("/").map(key => encodeURIComponent(key)).join("/")
const apiUrl = new URL(
 `https://${params.bucket}.s3.amazonaws.com/${encodedKey}`
)
Comment options

Hello! Reopening this discussion to make it searchable.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
response-requested Waiting on additional info and feedback. Will move to \"closing-soon\" in 7 days.
Converted from issue

This discussion was converted from issue #4859 on June 20, 2023 19:25.

AltStyle によって変換されたページ (->オリジナル) /