Skip to content

Missing response-content-disposition response header in CloudFront requests #3609

@git-hyagi

Description

@git-hyagi

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=1764876433

Possible 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

def generate_presigned_url(
returns the query parameter with the expected format, while with cloudfront
def generate_presigned_url(self, url, date_less_than=None, policy=None):
the query parameter is not modified .

SDK version used

1.42.2

Environment details (OS name and version, etc.)

Red Hat Enterprise Linux release 9.7

Metadata

Metadata

Assignees

Labels

cloudfrontp3This is a minor priority issueservice-apiThis issue is caused by the service API, not the SDK implementation.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions