[Rails] send_file problem

Duzenbury, Rich rduzenbury at BILtd.com
Tue Aug 8 20:53:27 GMT 2006


> 
> I'm attempting to use send_file to send an image file from
> public/images.  The file is world readable.
> 
> I've tried supplying the full path, but it still can't seem to find
the
> file.  Here is the code that calls send_file:

I don't think I would do this on purpose, if the image is really in
public.  If you want to change the image name at run time, I would
instead generate an <img> tag in the view, which the browser will then
download (per the setting of the user).

Otherwise, if you wish to send content that is *not* inside your web
(e.g. in /public or below) then you might do something like:

Config/routes.rb
# Force all requests for /protected/whatever to the render_static
method.
# Static content control
  map.connect '/protected/*path', :controller => 'protected',
                                  :action => 'render_static'

def render_static
    # see routes.rb.  A map is made so that the URL is 
    # visible as params[:path]
    requested_file = params[:path].to_s
    
    safe, opf = safe_to_send?(requested_file)
    if safe
      # compute the mime type and send the content here
      # This should be made much more concise (a hash lookup)
      # and extended to cover more mime types
        if requested_file =~ /\.pdf$/ then
          apptype = "application/pdf"
        elsif requested_file =~ /\.ppt$/ then
          apptype = 'application/vnd.ms-powerpoint'
        elsif requested_file =~ /\.swf$/ then
          apptype = 'application/x-shockwave-flash'
        else
          apptype = 'octet/stream'
        end
        send_file opf, :type=> apptype, :disposition=> "inline"
    end
end

protected
  def safe_to_send?(requested_file)
    # Absolute file location.  Beware that funny business with RAILS
ROOT 
    # (e.g. symbolic links) could throw this off
    output_base = File.expand_path RAILS_ROOT
    output_file = File.expand_path output_base + '/secret/' +
requested_file
    
    # Attempt to foil directory traversal attacks
    # make sure that output base is the beginning portion of output_file

    # I *think* rails makes this check unnecessary, but hey...
    result = false
    if output_file.index(output_base) == 0 and File.exists?(output_file)
      result = true
    end
    
    return result, output_file
  end

Note that I'm being paranoid about traversal attacks.  I believe rails
already deals with all of the . and .. stuff when the URL is submitted
by the browser, however, I could be wrong.

In this case, the protected content is in a subdirectory called
'secret', which is one level below the main root of the rails app.

Regards,
Rich


More information about the Rails mailing list