-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Missing response-content-disposition response header in CloudFront requests #3609
Description
Describe the bug
When generating presigned URLs for CloudFront distributions, query parameters are not being transformed from their API parameter format (ResponseContentDisposition) to HTTP query string format (response-content-disposition).
Regression Issue
- Select this option if this issue appears to be a regression.
Expected Behavior
When calling generate_presigned_url() (from CloudFrontSigner) method with a URL containing query parameters like ResponseContentDisposition, the parameter should be transformed to its HTTP query string format (response-content-disposition) to match the S3 API specification defined in the service model at botocore/data/s3/2006-03-01/service-2.json:
"ResponseContentDisposition":{
"shape":"ResponseContentDisposition",
"documentation":"<p>Sets the <code>Content-Disposition</code> header of the response.</p>",
"location":"querystring",
"locationName":"response-content-disposition"
}Current Behavior
The generate_presigned_url() method does not perform any query parameter transformation, resulting in URLs with incorrectly formatted query parameters and not returning a response with the response-content-disposition header.
Reproduction Steps
import os
import boto3
from botocore.signers import CloudFrontSigner
from datetime import datetime, timedelta
# Using CloudFront Signer
key_id="1234567"
rsa_signer = lambda message: b'signed'
cloudfront_signer = CloudFrontSigner(key_id, rsa_signer)
url_with_params = "https://test.cloudfront.net/myfile.pdf?ResponseContentDisposition=attachment%3Bfilename%3D%22download.pdf%22"
url = cloudfront_signer.generate_presigned_url(
url_with_params,
date_less_than=datetime.now() + timedelta(hours=1)
)
print(f"CLOUDFRONT: {url}")
# Using S3 Client (for comparison)
os.environ['AWS_ACCESS_KEY_ID'] = 'testing'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'testing'
s3_client = boto3.client('s3')
url = s3_client.generate_presigned_url(
'get_object',
Params={
'Bucket': 'mybucket',
'Key': 'myfile.pdf',
'ResponseContentDisposition': 'attachment; filename="download.pdf"'
},
ExpiresIn=3600
)
print(f"S3: {url}")output:
CLOUDFRONT: https://test.cloudfront.net/myfile.pdf?ResponseContentDisposition=attachment%3Bfilename%3D%22download.pdf%22&Expires=1764865633&Signature=c2lnbmVk&Key-Pair-Id=1234567
S3: https://mybucket.s3.amazonaws.com/myfile.pdf?response-content-disposition=attachment%3B%20filename%3D%22download.pdf%22&AWSAccessKeyId=testing&Signature=YCR4VzzJ2QzItrKyL4Q6%2F89nxuw%3D&Expires=1764876433Possible Solution
No response
Additional Information/Context
Our project uses django-storages and for requests with s3, the response-content-disposition header is returning as expected in:
https://github.com/jschneier/django-storages/blob/ca89a94a7462a2423df460e7bfd5f847457042ca/storages/backends/s3.py#L697-L699
but with CloudFront, this part https://github.com/jschneier/django-storages/blob/ca89a94a7462a2423df460e7bfd5f847457042ca/storages/backends/s3.py#L685-L687 is not returning the header and the query parameter is also not updated.
We believe this is an issue in botocore because in s3
Line 756 in 4e6ef43
| def generate_presigned_url( |
Line 407 in 4e6ef43
| def generate_presigned_url(self, url, date_less_than=None, policy=None): |
SDK version used
1.42.2
Environment details (OS name and version, etc.)
Red Hat Enterprise Linux release 9.7