diff --git a/packages/s3-request-presigner/README.md b/packages/s3-request-presigner/README.md index 5fd6ec91f424..94af66e42b95 100644 --- a/packages/s3-request-presigner/README.md +++ b/packages/s3-request-presigner/README.md @@ -34,6 +34,10 @@ You can get signed URL for other S3 operations too, like `PutObjectCommand`. `expiresIn` config from the examples above is optional. If not set, it's default at `900`. +If your request contains server-side encryption(`SSE*`) configurations, because +of S3 limitation, you need to send corresponding headers along with the +presigned url. For more information, please go to [S3 SSE reference](https://docs.aws.amazon.com/AmazonS3/latest/dev/KMSUsingRESTAPI.html) + If you already have a request, you can pre-sign the request following the section bellow. @@ -50,7 +54,7 @@ const signer = new S3Presigner({ credentials: credentialsProvider, sha256: nodeSha256, //if the signer is used in browser, use `browserSha256` then }); -const url = await signer.presign(request); +const presigned = await signer.presign(request); ``` ES6 Example: @@ -64,7 +68,7 @@ const signer = new S3RequestPresigner({ credentials: credentialsProvider, sha256: nodeSha256, //if the signer is used in browser, use `browserSha256` then }); -const url = await signer.presign(request); +const presigned = await signer.presign(request); ``` To avoid redundant construction parameters when instantiating the s3 presigner, @@ -77,3 +81,12 @@ const signer = new S3RequestPresigner({ ...s3.config, }); ``` + +If your request contains server-side encryption(`x-amz-server-side-encryption*`) +headers, because of S3 limitation, you need to send these headers along +with the presigned url. That is to say, the url only from calling `formatUrl()` +to `presigned` is not sufficient to make a request. You need to send the +server-side encryption headers along with url. These headers remain in the +`presigned.headers` + +For more information, please go to [S3 SSE reference](https://docs.aws.amazon.com/AmazonS3/latest/dev/KMSUsingRESTAPI.html) diff --git a/packages/s3-request-presigner/src/presigner.spec.ts b/packages/s3-request-presigner/src/presigner.spec.ts index b9f0896198b1..e4ee300c7e3c 100644 --- a/packages/s3-request-presigner/src/presigner.spec.ts +++ b/packages/s3-request-presigner/src/presigner.spec.ts @@ -87,4 +87,19 @@ describe("s3 presigner", () => { [EXPIRES_QUERY_PARAM]: "900", }); }); + + it("should disable hoisting server-side-encryption headers to query", async () => { + const signer = new S3RequestPresigner(s3ResolvedConfig); + const signed = await signer.presign({ + ...minimalRequest, + headers: { + ...minimalRequest.headers, + "x-amz-server-side-encryption": "kms", + }, + }); + expect(signed.headers).toMatchObject({ + "x-amz-server-side-encryption": "kms", + }); + expect(signed.query?.["X-Amz-SignedHeaders"]).toEqual(expect.stringContaining("x-amz-server-side-encryption")); + }); }); diff --git a/packages/s3-request-presigner/src/presigner.ts b/packages/s3-request-presigner/src/presigner.ts index 437ededa5e11..5525c6be2bd3 100644 --- a/packages/s3-request-presigner/src/presigner.ts +++ b/packages/s3-request-presigner/src/presigner.ts @@ -31,13 +31,22 @@ export class S3RequestPresigner implements RequestPresigner { public async presign( requestToSign: IHttpRequest, - { unsignableHeaders = new Set(), ...options }: RequestPresigningArguments = {} + { unsignableHeaders = new Set(), unhoistableHeaders = new Set(), ...options }: RequestPresigningArguments = {} ): Promise { unsignableHeaders.add("content-type"); + // S3 requires SSE headers to be signed in headers instead of query + // See: https://github.com/aws/aws-sdk-js-v3/issues/1576 + Object.keys(requestToSign.headers) + .map((header) => header.toLowerCase()) + .filter((header) => header.startsWith("x-amz-server-side-encryption")) + .forEach((header) => { + unhoistableHeaders.add(header); + }); requestToSign.headers[SHA256_HEADER] = UNSIGNED_PAYLOAD; return this.signer.presign(requestToSign, { expiresIn: 900, unsignableHeaders, + unhoistableHeaders, ...options, }); }