Restricting Access to Objects Stored on Amazon S3

Philipp Hansch edited this page Mar 28, 2018 · 14 revisions

How permissions work by default.

By default paperclip sets permissions for all AS3 uploads as :public_read ('public-read' in v5.0+). This means your files are totally wide open (i.e. they can be accessed by anyone simply by pasting the AS3 url into the browser).

What if you need to restrict access?

Restricting access can be a bit tricky. It's easy enough to have Paperclip mark your file as private when you store it on S3, but when it comes time to serve the file, its private status means the client won't be able to get it directly from S3. Typically, you'll resolve this by proxying the download via your own app, i.e. the request-response cycle looks like this: client/browser/app -> Rails -> S3 -> Rails -> client/browser/app. Setting up access control is therefore a three step process:

1. Configure your paperclip model to have private S3 upload

  • S3 storage

    has_attached_file :annual_report,
     :storage => :s3,
     :s3_credentials => "#{Rails.root}/config/s3.yml",
     :s3_permissions => :private,
     :path => "photos/:id/:filename"
  • FOG storage

    has_attached_file :lib, 
      storage: :fog,
      fog_credentials: {
        provider: 'AWS',
        aws_access_key_id: Rails.application.secrets.aws_access_key,
        aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
        region: 'region',
        scheme: 'https'
      },
      fog_directory: 'bucket_name',
      fog_options: { multipart_chunk_size: 10.megabytes },
      fog_host: nil,
      fog_public: false,
      path: "photos/:id/:filename"

At this point, any uploaded annual_reports should be inaccessible.

2. Create a download action in your controller:

def download
  redirect_to @department.annual_report.expiring_url(10)
end

This action simply catches the request and redirects to the AS3 url. But notice the expiring_url method call. This is where the magic happens. The method creates a temporarily authenticated url set to expire after 10 seconds. Now the only way to access the annual_report is through the download action. Note: If you are using fog as your provider, pass the expiration time rather than the number of seconds until the url expires, i.e. Time.now + 10.seconds.

Assuming you've set things up correctly (don't forget about routing), you can now use this action in your views:

<%= link_to "View Annual Report", download_annual_report_path(@department) %>

3. Setup authorization for the download action

Not worth going into details here. But most solutions would involve some kind of before_filter which checks if the current user is allowed to download the file in question. If your authorization requirements are at all complicated I would suggest checking out CanCanCan.

Other helpful links

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.