aws-sdk-js-v3
aws-sdk-js-v3 copied to clipboard
s3-request-presigner: `Content-Type` is not signed by default when specified
Describe the bug
The Content-Type
header is unsigned by default.
Your environment
SDK version number
@aws-sdk/[email protected]
Is the issue in the browser/Node.js/ReactNative?
Node.js
Details of the browser/Node.js/ReactNative version
v12.22.1
Steps to reproduce
Create a presigned PUT URL like
const putObjectRequest: PutObjectRequest = {
Bucket: 'uptrust-spencer-test',
Key: 'my/cool/object.txt',
ContentMD5: 'fTRbDyK9f+FJrC8Ulj2+4w==',
ContentType: 'text/plain',
ContentLength: 123,
};
const command = new PutObjectCommand(putObjectRequest);
const signedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600,
});
Observed behavior
The URL resulting from the config above has the query param
X-Amz-SignedHeaders=content-length%3Bcontent-md5%3Bhost
That is, three signed headers: content-length, content-md5, host.
Expected behavior
I expected those three, plus content-type. Rationale: Why would content-type be different than the other two content-_____
headers?
Screenshots
n/a
Additional context
Looks like content-type is excluded explicitly by default here
Workaround: Include content-type in signableHeaders
, which will override the default omission:
const signedUrl = await getSignedUrl(s3Client, command, {
expiresIn: 3600,
// new; must be lower case!
signableHeaders: new Set(['content-type']),
});
If there's a good reason for omitting it by default, could the default behavior + rationale be documented?
Hi @spencerwilson, thanks for opening this issue. Some of the headers are not signed by default to prevent signature mismatch errors. However, you can specify which headers you would like to sign when calling getSignerUrl(). Please see my example below:
import {getSignedUrl} from "@aws-sdk/s3-request-presigner";
import {PutObjectCommand, S3Client} from "@aws-sdk/client-s3";
const s3Client = new S3Client({
region: 'us-east-2'
});
const command = new PutObjectCommand({
Bucket: 'BUCKET',
Key: 'KEY',
ContentMD5: 'MD5',
ContentType: 'CONTENT-TYPE',
ContentLength: 123
});
const headersMap = new Map();
headersMap.set('content-type', true);
const url = await getSignedUrl(s3Client, command, {
expiresIn: 3600,
signableHeaders: headersMap
});
console.log('URL: ', url);
And here we can see the signed headers:
X-Amz-SignedHeaders=content-length%3Bcontent-md5%3Bcontent-type%3Bhost
Thanks!
If there's a good reason for omitting it by default, could the default behavior + rationale be documented? @spencerwilson I will bring this to discussion to the team to provide you a valid answer for this.
Thanks!
Hi @spencerwilson, after doing some researches I found that that header "content-type" along with others are black listed by default to prevent signature mismatch errors. I could not find any public documentation but we can confirm that by checking other SDKs as JS V2 and PHP where we can see that content-type is defined as an unsignable header.
Thanks!
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.