Working with files in Rails
Tuesday, March 27, 2007 at 11:31PM In many cases web applications save user entered data to files and deliver file uploads. You should always filter file names that come directly from the user, as an attacker could use a malicious file name to download or overwrite any file on the server. If you use a file name that the user entered without filtering, for example, in a send_file() method, which sends files from the server to the client, then any file can be downloaded:
send_file( params['filename'] )
Even if you use send_file( '/var/www/uploads/' + params['filename'] ), you can download any file, as every directory in Unix has a link to its parent directory, and a file name of ../../../etc/passwd downloads the system's login information.
When it comes to filtering file names, don't try to remove malicious user input, as there are very many possibilities to hide, for example, the parent directory link “../” to your filter. An attacker could use a different encoding, such as “%2e%2e%2f”, which the operating system might or might not understand. Or think of a situation where you delete all “../” and an attacker uses a string such as “....//”. So it is best to use the whitelist approach, which checks for validity of a file name with a set of accepted characters. And in case it isn't a valid file name, reject it, and don't try to filter out malicious parts.
A second step is to validate that the file name is in the expected directory. Example in a controller storing the files with attachment_fu:
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename =!
File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, :disposition => 'inline'
Another approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. Think of a situation where an attacker uploads a file “file.fcgi” with code in it, which will then be executed when someone downloads the file. Of course, this does only happen if you store the uploaded files in a subdirectory of Apache's DocumentRoot directory (Rails “public” directory normally).
On the other hand, when you allow users to name the file on disk, an attacker could overwrite important files or add new ones. That's why web server processes should be run as non-root user, so an attacker cannot overwrite any file. FastCGI, for example, as set up in the Apache chapter, runs as the “apache” user, which shouldn't have write permissions on files and directories. Only an upload directory (and some Rails directories as described in the Apache chapter) should be writable to that user.
Heiko |
2181 Comments | 

Reader Comments (2181)
Might be worth talking about X-Sendfile too...
Great post!.
The database approach is terrific.
Farming for (world of warcraft gold) isn't easy, but you can buy cheap wow gold, wow power leveling on sale. We have revolutionized the exchange of money to wow gold with fast delivery. Purchase online and wow power up your character to the next level. Our customer service will make sure you are more than satisfied with every purchase. Feel free to contact our 24 hour live support with any questions you may have. Welcome to our website,delivery in 24 hours,7/24 service.buy cheap wow gold now,I think you will have your pleasure. and .
, buy eve isk, Cheap eve isk. We provide cheap eve isk to reliable customer.
, eve online isk, buy eve online isk, Cheap eve online isk.
Buy ,cheap ffxi gil,cheapest ffxi gil,ffxi gil sale.We supply cheap ,eq2 platinum and eq2 plat gold, eq2 plat,so you buy cheap eq2 plat gold, eq2 plat accounts. is the most valuable form of currency in Lineage 2(L2 Adena). We provide cheap eve online isk to reliable customer.Cheap for sale,lowest price gold for wow,as low as $19/1000 Gold..Buy world of warcraft gold here.