Protecting the content of public/ in a Rails app

假装没事ソ 提交于 2019-11-28 04:34:39

I've done this on a site where people pay to download certain files, and the files are stored in RAILS_ROOT/private. The first thing to know is that you want the web server to handle sending the file, otherwise your app will be held up transmitting large files and this will quickly bring your site to a halt if you have any kind of download volume. So, if you need to check authorization in a controller, then you also need a way to pass control of the download back to the web server. The best way of doing this (that I know of) is the X-Sendfile header, which is supported by Nginx, Apache (with module), and others. With X-Sendfile configured, when your web server receives a X-Sendfile header from your app, it takes over sending the file to the client.

Once you have X-Sendfile working for your web server, a private controller method like this is helpful:

## # Send a protected file using the web server (via the x-sendfile header). # Takes the absolute file system path to the file and, optionally, a MIME type. # def send_file(filepath, options = {})   options[:content_type] ||= "application/force-download"   response.headers['Content-Type'] = options[:content_type]   response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filepath)}\""   response.headers['X-Sendfile'] = filepath   response.headers['Content-length'] = File.size(filepath)   render :nothing => true end 

Then your controller action could look something like this:

## # Private file download: check permission first. # def download   product = Product.find_by_filename!(params[:filename])   if current_user.has_bought?(product) or current_user.is_superuser?     if File.exist?(path = product.filepath)       send_file path, :content_type => "application/pdf"     else       not_found     end   else     not_authorized   end end 

Obviously your authorization method will vary and you'll need to change the headers if you're offering files other than PDFs or you want the file to be viewed in the browser (get rid of application/force-download content type).

You could use Amazon S3. You could use the controllers to generate and serve up the urls behind your secure area, and it also has a feature that basically makes resources available only for a certain amount of time once a url is generated.

Check out this url: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html

Elthariel

AFAIK, X-SendFile is not supported by nginx. Nginx has its own extension allowing this, called X-Accel-Redirect.

You will find more information about this here : https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/

There is also a rails plugin implementig this feature, on github: goncalossilva/X-Accel-Redirect

If you want to tie content delivery with your Rails authentication and authorization system, then you essentially have to put the content behind a controller.

If you are looking at a more simple login approach, you can handle it with HTTP Auth and settings in your hosting environment (using htaccess, for example).

Making the file available at an unpredictable URL is a simple solution currently used in some production systems.

E.g.: GitLab. The following image was uploaded to an issue of a private repository, https://gitlab.com/cirosantilli/test-private/issues/1, but you can still see it:

Note the unguessable 90574279de prefix automatically added to the URL.

Bitbucket (non-Rails) also uses this technique.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!