I’m having trouble generating signed URLs for S3 objects using Python 3.7. Specifically, URLs for objects with spaces in their keys result in “Access Denied” errors, while URLs for objects without spaces generally work fine. However, not all objects without spaces work correctly, and objects with spaces consistently fail.
from datetime import datetime ,timedelta
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from botocore.signers import CloudFrontSigner
class SecuringCloudFront:
def __init__(self):
pass
def rsa_signer(self, message):
try:
with open('private_key.pem', 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
return private_key.sign(message, padding.PKCS1v15(), hashes.SHA1())
except Exception as e:
print(f"Error loading or signing with private key: {e}")
return None
def get_document_images_from_s3(self, images_keys, images_to_get=None):
"""
Description: creates signed CloudFront URLs for document images
Author: Safwen Inoubli
Attributes:
- images_keys: list of keys
- images_to_get: list of images index
Result: list of URLs
"""
CLOUDFRONT_URL = "https://***********.cloudfront.net"
KEY_PAIR_ID = "********" #id of stored key
images_urls = []
keys = (
images_to_get
if images_to_get is not None
else list(range(len(images_keys)))
)
for key in keys:
try:
if key >= len(images_keys) or len(images_keys[key]["name"]) == 0:
continue
s3_key = images_keys[key]["name"]
url = f"{CLOUDFRONT_URL}/{s3_key}"
expire_date = datetime.utcnow() + timedelta(hours=24)
cloudfront_signer = CloudFrontSigner(KEY_PAIR_ID, self.rsa_signer)
signed_url = cloudfront_signer.generate_presigned_url(
url, date_less_than=expire_date
)
url = {"url": signed_url} if signed_url else {"url": ""}
except Exception as e:
url = {"url": ""}
print(f"Error generating signed URL: {e}")
images_urls.append(url)
return images_urls
if __name__ == "__main__":
securing_cloud_front = SecuringCloudFront()
images_keys = [
{"name": "imageIns3.jpg"}
]
signed_urls = securing_cloud_front.get_document_images_from_s3(images_keys)
print("Signed URLs:", signed_urls)
Replacing Spaces with %20 and +:
I attempted to replace spaces in object keys with %20 and + in the URL. This approach did not resolve the issue and I still received “Access Denied” errors.
Integrating Custom Policy:
I tried integrating a custom policy for signed URL generation but could not understand how to apply it correctly. Despite various attempts, I was unable to resolve the problem using this method.
Ben ahmed Mansour is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.