Setting Content-Disposition to attachment using Ruby on Rails and Paperclip - ruby-on-rails

I have a – hopefully small – problem.
I am using Ruby on Rails and Paperclip to handle file uploads.
Now I want to automatically set the Content-Disposition header to "attachment" so that when the user clicks a link, the file is downloaded instead of shown directly in the browser.
I found the following solution for Amazon S3:
Download file on click - Ruby on Rails
But I don't use S3.
Can anybody help?
Thanks in advance,
/Lasse

If you use File Storage, Paperclip stores the files within the RAILS_ROOT/public/system folder (configurable using the :path option).
Files from the /public folder are served directly as static files. "Rails/Rack never sees requests to your public folder" (to quote cwninja).
The files from the /public folder are served by the webserver running this app (for example Apache or WEBrick in development). And the webserver is responsible for setting the headers upon serving the file. So you should configure the webserver to set the correct headers for your attachment.
Another option is to build a controller or some Rack middleware to serve your paperclip attachments. There you can do something like response.headers['Content-Disposition'] = 'attachment'.
Third option is to use S3, then you can store headers (like Content-Disposition) within the S3-object. S3 then serves the paperclip attachment using those headers.

According to this link, you can do the following:
<Files *.xls> ForceType application/octet-stream Header set Content-Disposition attachment </Files>
<Files *.eps> ForceType application/octet-stream Header set Content-Disposition attachment </Files>

Related

How can I block file uploads in Rails?

I have a rails app (v4.2). I have two actions that permit an image upload using paperclip. I have paperclip validation on the mime types.
Anti-malware on the server found a bunch of PHP files in /tmp like this one:
/tmp/RackMultipart20190610-9668-u9nebk.php
I assume they are created in the file upload process.
Two questions:
How can I track down where they came from? Looking in my production.log, I see a bunch of 404s for posts to bogus joomla & wordpress .php paths but nothing that could have been responsible for these uploads.
How can I prevent them in the future?
I'm using rack attack and can block .php file extensions but how can I block file uploads in forms?
We have two places where signed in members can upload images or PDFs. How can I block all other attempts to upload files?
File uploading by-pass is a common technique for uploading webshell's and other stuff.
There are 2 basic methods that will help you to decrease the amount of file uploaded to your server:
MIME Content-type validation: If you validate the content-type of the uploaded file you (since you just want images) you can assure that only image-type files are uploaded:
:content_type => ["image/gif", "image/jpg", "image/jpeg", "image/png", "image/bmp", "image/x-bmp"]
But this still can be bypassed, so you need to add another verification:
File extension validation: You also should add a file extension validation to assure you only permit image-type extensions to your upload.
I've find a cool post where it shows a good implementation of file extension validation: https://stevenyue.com/blogs/validate-attachment-file-size-and-type-in-rails/
Make sure you implement both of these techniques and you should be fine.

Rails5: Open excel file in browser

I am using shrine for excel file uploading. File is successfully uploaded and linked to model instances using shrine.
I wanted to open that Excel file(.xls, .xlsx) in browser instead of downloading it. I have looked for various solutions and tried them, but no luck.
The recommended solution from most of the people is using 'send_data', I have tried with that also:
send_file(data, type: 'application/vnd.ms-excel', filename: "#{uploaded_file.metadata["filename"]}", disposition: 'inline')
But it is downloading the file instead of opening it directly in browser.
My questions are
1) Is this opening/downloading file depends on browser settings?
2) Which one is better? To open a file in browser OR use gem like 'axlsx' to create and render excel templates in app?
Thanks!
Whether the browser will attempt to open the file in the browser or download it is determined by the Content-Disposition response header.
Content-Disposition: inline # browser will attempt to display it
Content-Disposition: attachment # browser will always download it
You can also specify the filename in both cases, should the user choose to download it:
Content-Disposition: inline; filename="table.xls"
Content-Disposition: attachment; filename="table.xls"
How to ensure that the Content-Disposition is specified to inline depends on where you're storing your files. If you're storing them on the filesystem, I think that the Rails::Static middleware already has the "inline" behaviour. If not, you can switch to download_endpoint.
If you're storing them on Amazon S3, you can specify the default :content_disposition upload option on Shrine::Storage::S3 initialization:
Shrine::Storage::S3.new(upload_options: {content_disposition: "inline"}, **options)

append query string while requesting for fonts in rails

Note: This problem is related to firefox being unable to download fonts from cross domain servers
source: mozilla
In Gecko, web fonts are subject to the same domain restriction (font files must be on the same domain as the page using them), unless HTTP access controls are used to relax this restriction. Note: Because there are no defined MIME types for TrueType, OpenType, and WOFF fonts, the MIME type of the file specified is not considered.
I have a rails application where assets are fetched from amazon cloudfront. CloudFront, in turn, fetches the assets from s3 bucket. I defined the asset_host in production.rb so that assets are fetched from the cloudfront url.
config.action_controller.asset_host = "//d582x5cj7qfaa.cloudfront.net"
Because I am serving assets from different domain i.e cloudfront. I am unable to download fonts in case of firefox. I came across this and applied the same logic as given in the answer, but I was still unable to download the fonts. The reason behind this is, since I have multiple domains (eg: abc.almaconnect.com, xyz.almaconnect.com) that use the same cloudfront url to access fonts, the cloudfront server caches the server headers that were sent in the first request and return the same header even when the domain requesting is different the next time.
Then I came across this link, which solves my problem of cache headers i.e if I pass query string along with the url it should work correctly
My question is, how do I append the query_string dynamically through the url? Since the same application is used by multiple domains, I need to real-time method to append query string. How do I do that?
For reference:
Here is the CORS configuration that I used in amazon s3 bucket, which was used by cloudfront url to serve the assets:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*.almaconnect.com</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Content-*</AllowedHeader>
<AllowedHeader>Host</AllowedHeader>
</CORSRule>
</CORSConfiguration>
This is how I came to know what happens with and without appending query_string to the url:
curl -i -H "Origin: https://niet.almaconnect.com" https://d582x5cj7qfaa.cloudfront.net/assets/AlmaConnect-Icons-d059ef02df0837a693877324e7fe1e84.ttf?https_niet.almaconnect.com
When will used the same curl request with different origins, without appending the query_string, the returned response contains the origin header that was used in the first request.

Carrierwave + Fog(s3). Letting the users to download the file

I am working on a project where I have to provide download links to user for the files which are stored in the s3. Initially I tried,
link_to "Download", #medium.file.url
But this opens the file directly on the browser. When i tried to download an mp4 file, chrome started playing it automatically. I don't want that to happen. So I am using send_file for this task,
def download
#medium = Medium.find(params[:id])
send_file #medium.file.url
end
In my local, I have set the storage to file and I have tested this, which works perfectly fine. But on staging, the files are served from s3, I am always getting ActionController::MissingFile. My app is hosted on heroku. I also want to know if using send_file is good choice or if there is a better way of doing this.
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
When i was googling, i found that nginx header setting for sending files should be enabled for production. I added the following line in config/environments/production.rb. Still no luck. I need some help on this. Thanks.
I think this is because S3 wants the file to be played.
I don't have access to my S3 here, but I solved the problem by changing the action for PDF files (which was shown my case) in the S3 control panel.
If you can fix this, I think you can avoid using send_file completely.
Edit: I managed to access S3 now. Go to properties -> Metadata, and add the key
Content-Disposition: Attachment
Then your file will always be downloaded instead of shown.

Download file on click - Ruby on Rails

My application is using Rails 2 backend, Heroku for hosting, Paperclip for file uploads, and Amazon S3 for file storage.
Right now users can upload files with paperclip + s3 - this works flawlessly. After upload, an icon appears on their dashboard, linked to the file location (in s3 bucket). When the icon is clicked, the browser opens the file in a new window (for most file types - PDF, MP3, img, etc). Instead of opening, I want the file to be automatically downloaded when the user clicks the file's icon (like Gmail attachments). The solution should be able to work for any file type and cross-browser.
Is there a helper to do this in rails, or is javascript needed? I'm really stuck on this one so anything to point me in the right direction would be greatly appreciated. Thanks!
Please try the following:
class Test < ActiveRecord::Base
has_attached_file :testfile,
:storage => :s3,
# All your S3 config
:s3_headers => {"Content-Disposition" => "attachment"}
end
This should tell the Paperclip Gem to set the "Content-Disposition" header to the value "attachment" for newly uploaded files.
Note that you have to manually edit the already uploaded file, e.g. with Cyberduck or another FTP Client.
When you transfer the file, you need to set a Content-Disposition header with a value of attachment; filename=yourfilename.pdf. If it's transfered directly from S3, you'll need to tell S3 to set the Content-Disposition headers as well. Possibly also Content-Type.
Note that if you tell S3 to associate a Content-Disposition header, it will always transmit this header.
FWIW, here's Amazon's documentation on doing a PUT for an Object: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html

Resources