My python (Django) application stores confidential documents and has a private bucket in AWS S3.
When you make a request for the resource, it uses boto3 to generate a pre-signed URL to be able to download the document.
The code to do so is as follows:
@staticmethod
def create_presigned_url(key_name, bucket_name=settings.AWS_PDF_STORAGE_BUCKET_NAME, expiration=60):
"""
Generate a pre-signed URL to share a PDF.
:param key_name: string.
:param bucket_name: string.
:param expiration: Time in seconds for the pre-signed url to remain valid.
:return: Pre-signed URL as a string. If error, returns None.
"""
s3_client = boto3.client('s3')
try:
response = s3_client.generate_presigned_url(
'get_object',
Params={'Bucket': bucket_name, 'Key': f"pdf/{key_name}"},
ExpiresIn=expiration
)
except ClientError:
return None
return response
When I run my application locally, it works great. It generates a link I can use to download the document and it expires after 60 seconds. Such URL looks like this:
https://BUCKETNAME.s3.amazonaws.com/pdf/testuser1/dummy.pdf?AWSAccessKeyId=ACCESSKEY&Signature=SIGNATURE&Expires=EXPIRATION"
I then deploy my application to AWS Elastic Beanstalk. All the same environment variables with AWS secrets are configured. But when I run the same request, I get a much longer URL that looks like this:
https://fiber-staging-pdfs.s3.amazonaws.com/pdf/liza/dummy.pdf?AWSAccessKeyId=ACCESSKEY&Signature=SIGNATURE&x-amz-security-token=TOKEN&Expires=EXPIRATION
Somehow, the same code when run against this environment is adding a x-amz-security-token parameter? Why? And When I try to visit this generated URL, I get a 403 Forbidden error.
The Bucket Policy for this bucket is like:
{
"Version": "2012-10-17",
"Id": "POLICYID",
"Statement": [
{
"Sid": "SID",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNTID:root"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKETNAME/*"
}
]
}