6

I am using django-storage (which uses Boto3 internally) to upload images. I am successfully able to do so and the return URL I get is of this format:

https://.s3.amazonaws.com/foo.jpg?Signature=&AWSAccessKeyId=&Expires=1513089114

where Signature and AWSAccessKeyId are filled in as well.

Now, I need to give this URL directly to Mobile Developers and I can't have the timeout set so late. I need it for many years or potentially always accessible. What is a good way to do so? What is the solution

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
tsaebeht
  • 1,570
  • 5
  • 18
  • 32
  • What Boto3 API returns a signed URL? Are you generating the URL after uploading? – helloV Dec 12 '17 at 15:32
  • yeah it is the returned URL – tsaebeht Dec 12 '17 at 15:43
  • 1
    Did you read my question fully? – helloV Dec 12 '17 at 15:45
  • Oh right, I'm using django-storages in Python that internally uses Boto3. django-storages.readthedocs.io/en/latest/backends/amazon-S3.html – tsaebeht Dec 12 '17 at 15:52
  • You should never hard-code a signed URL into an app... or anything else you can't easily update. The URL not only expires eventually (and cannot be un-expired), but also, when you deactivate or rotate out your AWSAccessKeyId (as you periodically should, and of course would need to if your secret were ever compromised) any URLs signed by that key are immediately invalidated. There is nothing insecure about *revealing* a signed URL, but shipping a product that relies on it remaining valid indefinitely would be a substantial mistake. – Michael - sqlbot Dec 12 '17 at 20:06
  • The URL is revealed by the API. We're using the django-storages API to store data and this is the URL returned by it. We don't want to waste time modifying it. It meets our small usecase. – tsaebeht Dec 13 '17 at 09:58

4 Answers4

11

On glancing through the django-storages S3 Docs , I see there is a provision for

AWS_QUERYSTRING_EXPIRE which states

The number of seconds that a generated URL is valid for.

So if you wanted the link to be valid for 5 years from now, you can just add the corresponding number of seconds here which would amount to 157784630

So in conclusion, just add the following in your settings.py

AWS_QUERYSTRING_EXPIRE = '157784630'

This doesn't really seem like good practice to me but more like a convenient hack/workaround instead.

gabbar0x
  • 4,046
  • 5
  • 31
  • 51
  • 9
    This will not work. AWS signed urls have a maximum lifetime of 7 days ([see this question](https://stackoverflow.com/questions/24014306/aws-s3-pre-signed-url-without-expiry-date)). Just because you can set it as 5 years in `django-storages`, doesn't mean AWS will honor it. – gatlanticus Aug 01 '19 at 07:20
  • I agree with gatlanticus, in such case AWS complains with the following error: AuthorizationQueryParametersError X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds .... .... – Lukasz Czerwinski Aug 16 '22 at 16:07
4

If your S3 bucket is public, you can use this setting to turn off query parameter authentication.

Setting AWS_QUERYSTRING_AUTH to False to remove query parameter authentication
from generated URLs. This can be useful if your S3 buckets are public.
hannah
  • 41
  • 7
2

The accepted answer achieved almost what I wanted. I didn't want to set it application wide, but only on a specific file. If you're like me, read on...

You can generate the expiration for a specific file field by using the storage's url() method which has an optional expire kwargs:

post = Post.objects.first()
post.image.storage.url(post.image.name, expire=60*60*24*365)

The downside of this is that it's incompatible with Django's default storage API, which would raise a TypeError locally:

TypeError: url() got an unexpected keyword argument 'expire'
Bruno A.
  • 1,765
  • 16
  • 17
1

Found better solution to make url for file in S3BotoStorage for 10 years available in Django FileField. In settings.py:

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' ...

And solution is:

from django.core.files.storage import default_storage
from myapp.models import MyModel

myobj = MyModel.objects.first()
key = default_storage.bucket.new_key(myobj.file_field.name)
url = key.generate_url(expires_in=60*60*24*365*10)

url will be valid for 10 years.

valex
  • 5,163
  • 2
  • 33
  • 40