- 
  Notifications
 
You must be signed in to change notification settings  - Fork 638
 
S3RequestPresigner#presign causes SignatureDoesNotMatch error when S3 object key contains = #4862
-
Checkboxes for prior research
- I've gone through Developer Guide and API reference
 - I've checked AWS Forums and StackOverflow.
 - I've searched for previous similar issues and didn't find any solution.
 
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
Beta Was this translation helpful? Give feedback.
All reactions
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
-
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!
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
I am going to convert this into a discussion so that people facing similar issue can find guidance regarding.
Thanks!
Beta Was this translation helpful? Give feedback.
All reactions
-
@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}`
)
Beta Was this translation helpful? Give feedback.
All reactions
- 
 
👍 1 
-
Hello! Reopening this discussion to make it searchable.
Beta Was this translation helpful? Give feedback.