Securing your downloads with mod_xsendfile

October 20, 2009 at 2:23am.

This entry is about Programming

14 comments.

I know I haven’t been the best blogger lately (in part because I’ve been blogging over at my new job). So to help remedy that I am going to try posting more short tips and snippets. These are things I want to be able to refer back to myself, and that I think might be useful to other people.

Why use mod_xsendfile?

In many case you have files available through your webapp that should only be accessible to certain people. You don’t want to rely on security through obscurity (throwing them all in a public folder, but only showing the links to certain visitors). Although that will deter casual users from getting ahold of the forbidden files, once someone has the link they could post it anywhere and your security is blown. What you want to do is to squirrel the files away outside of your publicly-accessible folder and then give them out as needed once a user is validated by whatever authentication method you are using.

Apache’s mod_xsendfile module (inspired by lighthttpd’s X-Sendfile) is the tool for this job. By intercepting http requests with the “X-Sendfile” header and outputting the contents of a specified file, it both enables per-file authentication and is more efficient than processing the file contents through your server-side script and sending it to the browser that way. The file path you give to mod_xsendfile doesn’t need to be in the server’s public folder, so it is a very secure way to store sensitive files.

Installing on Mac OS X

(Installing on any Apache server will be similar, but I use OS X for development, so that is what these instructions are based on.)

Prerequisites:

  • Xcode Developer Tools (which is can be installed from your OS X DVD)
  • Basic knowledge of the command line

Download the source

Open up the Terminal and create a folder to hold the mod_xsendfile source files:

mkdir ~/src
cd ~/src

Now download the source and uncompress it:

curl -O http://tn123.ath.cx/mod_xsendfile/mod_xsendfile-0.9.tar.gz
tar xzvf mod_xsendfile-0.9.tar.gz
cd mod_xsendfile-0.9

Compile and install it:

sudo apxs -cia mod_xsendfile.c

You will have to enter your password and it will do its thing. Finally you need to enable the module. You can choose to do this globally in your httpd.conf file or for just a particular project in a .htaccess file. Either way, the syntax is the same:

# Enable mod_xsendfile
XSendFile On
# Allow sending files from above the requested uri
XSendFilePath /absolute/path/to/the/files/

(XSendFilePath is only necessary if the files you want to serve using mod_xsendfile are outside of your public web root folder.)

Finally restart Apache by opening your System Preferences and in the Sharing pane unchecking and re-checking the “Web Sharing” option.

Sending a file

This part is easy. Just set the x_sendfile header with the path to the file you want to send. Make sure you check first if the current user is allowed to access the file! They will not see the xsendfile header or have any way of knowing where the file is actually stored.

In PHP

header("X-Sendfile: $filePath");
header("Content-Disposition: attachment; file=$fileName");

In Ruby on Rails

response.headers['X-Sendfile'] = @file.path
response.headers['Content-Disposition'] = "attachment; filename=#{@file.file_name}"
render :nothing => true

Comments:

Anand gravatar

Anand on November 1, 2011 at 7:45am#1

I get this problem in ubuntu when I try to restart apache.

Invalid command ‘XSendFileAllowAbove’

EliVZ gravatar

EliVZ on November 2, 2011 at 2:08am#2

Anand- It seems that the configuration options for mod_xsendfile have changed since I wrote this tutorial. Now you need to use XSendFilePath with the absolute path to the directory where you will serve the files from, instead of XSendFileAllowAbove. Something like:

XSendFilePath /var/www/sitename.com/protectedfiles/

I will update the post.

Jácome gravatar

Jácome on November 17, 2011 at 5:23pm#3

Hi!

Thanks by the post, but I believe version 0.9 works with XSendFileAllowAbove and after 0.10 with the new XSendFilePath (https://tn123.org/mod_xsendfile/).

In fact, in my Mac I can run my app with the 0.9 version, but not with versions with XSendFilePath. Any ideas why?

Thanks

EliVZ gravatar

EliVZ on November 18, 2011 at 7:13pm#4

Jácome- You are correct, XSendFilePath is a more recent addition. If you are still using v0.9 you will need to following line instead:

XSendFileAllowAbove on

Jonatan gravatar

Jonatan on September 8, 2012 at 5:06pm#5

Excuse me, but with Mountain Lion and Xcode 4.4 it doesn’t seem to install. I try to run “sudo apxs -cia mod_xsendfile.c”, but it answers with the following error:
“env: /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.8.xctoolchain/usr/bin/cc: No such file or directory
apxs:Error: Command failed with rc=65536”

I’ve tried to download and install the Xcode Command Line Tools from the Preferences pane, but this doesn’t help. I’ve followed these instructions on Lion and an older version of Xcode, and it worked really great!
So anyway, thanks for the help!

Jonatan gravatar

Jonatan on September 8, 2012 at 6:04pm#6

OK, with some research done it seems that Xcode 4.4 has moved some of its commands, so a symlink fix this issue.
“sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.8.xctoolchain”

EliVZ gravatar

EliVZ on September 8, 2012 at 9:03pm#7

Thanks for that fix, Jonatan! I haven’t had to re-install it yet since I upgraded to 10.8, so I didn’t realize that had changed.

Bala Paranj gravatar

Bala Paranj on January 9, 2013 at 8:32pm#8

I am using CarrierWave with Rails 3.2. My question is :

Can I point to a directory with a wild card something like this:

/apps/myproject/shared/uploaded/files/*

Because I want to serve files under the files folder which have subdirectory underneath for every user account.

EliVZ gravatar

EliVZ on January 10, 2013 at 1:49am#9

Bala- You should be able to use the directive: XSendFilePath /apps/myproject/shared/uploaded/files and be able to send files from any subdirectory of that path. That said, I’ve never tried it myself so I don’t know if there are any caveats to be aware of.

Kevin gravatar

Kevin on April 17, 2013 at 5:47pm#10

Hi, I have an this error in the apache error_log trying to make mod_xsendfile work in OS X 10.7.3

[notice] child pid 18392 exit signal Bus error (10)

Anyone knows a fix? Thanks

Matthias gravatar

Matthias on May 15, 2013 at 5:22am#11

same error as Keven here. Please help!!

mike gravatar

mike on June 21, 2013 at 9:26am#12

Hi Folks,

I have mod_xsendfile up and running on a centos server.

I have the situation that we display a image gallery for logged in users. Now the imagelist is growing and we have about 80 pictures on a single page that are served by mod_xsendfile. This still works, but from what I see, apache starts a process for every mod_xsendfile request. That means for displaying the image gallery, apache starts 80 processes.

Is this common behaviour or is there a better way to do this?

Regards
Mike

EliVZ gravatar

EliVZ on June 22, 2013 at 1:43pm#13

Mike- I must admit that I don’t know much about Apache’s internal handling of Xsendfile, and I’ve also never used the technique for more than one file at a time. Perhaps someone else will be able to chime in with a suggestion….

mike gravatar

mike on June 24, 2013 at 7:49am#14

Hey,
thanks for the answer. If I find some useful information, I will post it here.
Regards
Mike

Got something to say?