Rails: How to protect images uploaded to S3 using dragonfly

1.8k views Asked by At

I'm looking for a way to protect images uploaded from a Heroku Rails 3 app using the dragonfly gem to a S3 storage. I'd like to control access on a user basis and ensure that the images can't be accessed directly.

I've found some information for other gems such as paperclip, but since dragonfly works a bit differently, I'm not sure what's the preferred way to deal with this case.

3

There are 3 answers

4
polarblau On BEST ANSWER

Since I’m currently using a routed endpoint, expiring urls unfortunately don’t work for me.

I found, that setting an x-amz-acl header to set the permissions, works in my case since all images are exclusively accessed through the application and never directly.

# config/initializers/dragonfly.rb
app = Dragonfly[:images]

if Rails.env.production?
  app.datastore.configure do |c|
    # […]
    c.storage_headers = {'x-amz-acl' => 'private'}
  end
end

Another way to do this programmatically for some images can be achieved using calling the method put_object_acl directly on the Dragonfly’s Fog storage instance, e.g. in a model callback:

app = Dragonfly[:images]
app.datastore.storage.put_object_acl 'bucket-name', model.image_uid, 'private'

This will of course work only if the storage in use is in fact a Fog storage, hence a check would be needed.

I don’t have any tests for this solution currently, since it seems to involve a lot of mocking. So, if anyone has some input on this solution, I would highly appreciate hearing about it!

1
microspino On

Dragonfly allows an :expires option for remote_url that is a wrapper for url_for of S3DataStore. See here. What about setting a restricted download link tha expires in 10 seconds as they do with paperclip?

You can also add random guid for your images inside their URL to provide more security as per this SO question.

Not a real solution, just some thoughts.

1
Neil Middleton On

You can use Amazon S3's expiring URLs. This is a signed URL that has an expiry time. Therefore you generate the URL, give it to the user, and it's up to them to read that file within the allotted time. For instance, if you're loading images within a page that need to be secure, set the expiry to 10 seconds or so.

Files must be stored as NOT world readable obviously.

From looking at Dragonfly this appears to be quite simple:

my_model.attachment.remote_url(:expires => 10.seconds.from_now)

More info here: http://markevans.github.com/dragonfly/file.DataStorage.html#S3_datastore

If that doesn't work, I know that Paperclip supports this behaviour as I've used it several times in the past.