tags:

views:

317

answers:

4

Is there a way to prevent someone from faking a mime type on a file upload and then running a php/exe/etc...

I have to make the file upload directory writeable and executable so that the files can be stored, but this allows anyone to run a script after. One thing I can do is add random data to the file name so they can't guess the file name after (since they still can't read from the directory to get a listing).

I'm using file upload with php for the first time and I'm trying to cover all of the security issues.

+2  A: 

The file upload directory should not be accessible to the web browser. I.e. don't allow somebody to upload a file, say "remove_all_my_files.php", and then execute it on your system by giving the url to it, say "http://xample.com/uploads/remove_all_my_files.php".

Paul Tomblin
So as long as I don't allow anything with .php and obfuscate the file name I should be okay.
dbrien
No, what he means is to upload the files above your "www" or public_html directory. i.e. something your server can access, but has no direct URL to.
Swati
It's safer just to not allow any http access to that directory than to rely on knowing all the various dangerous file extensions. Remember what a shock it was when people discovered all the file extensions that Windows would execute?
Paul Tomblin
Not providing access to the uploaded file is part of the solution.
Till
A: 

The information in $_FILES always comes from the client, so what you want to do is accept the file and scan it on the server. I'd either recommend using finfo, which is a PHP extension and it makes it easy:

<?php
// example :-)
$finfo = finfo_open(FILEINFO_MIME);
echo finfo_file($finfo, '/path/to/your/upload/file);
finfo_close($finfo);
?>

There is also a OO-interface if you don't like procedural.

If finfo is not an option, you could use the unix command file to check.

Also, many people suggest serving files through a wrapper. I am torn on this one, it can be a solution but it's far from ideal because a) the files are still on your server and b) it's expensive to serve files like that.

Till
A: 

On my Apache web server configurations I don't believe the actual file contents determine whether a file runs as a script or not. The determination as to whether to display a file as text or an image format, or run it as a script is made by matching the file ending.

For example, a directive in the apache configuration file, httpd.conf of

AddType application/x-httpd-php .php

tells the server to run files ending in .php to run as a php script. So just make sure that none of the uploaded files are saved with a .php ending, or any other script executable ending or any file ending you use for include files.

Devin Ceartas
With a shell call, you can run (= execute) anything. Or make anything run. You can also run .txt. The file extension does not matter. It's a windows thing where people assume .exe runs, it's really about permissions.
Till
In the context of a web application, where are you going to get the shell interpreter to make that call? You'd need shell access to do that wouldn't you? You can't just point a web browser at a shell script or php script and tell it "go" without the web server being configured to allow that
Devin Ceartas
Ever heard of XSS?
Till
The point is that you should always preventeven unlikely things from happening.File extensions aren't to do with execution, so assume that any file can be executed, no matter how unlikely that event is.
Rich Bradshaw
Yes, clearly I wouldn't have a directory which was uploadable and run PHP scripts from it. But the question presumed you had to. You can not use XSS to force the execution of a php file stored under a different extension. XSS is client side scripting problem, PHP is NOT a client side scripting lang.
Devin Ceartas
XSS = Cross Side Scripting, has nothing to do with client-side or server-side. It's about code injections in general. Those also happen on the server-side.
Till
A: 

Don't serve the file directly. Keep uploads in a no public access location. Read from file, buffer for output to allow downloading.

Here's the basic idea:

function ReadAndOutputFileChunked ($filename) { 
 $chunksize = 1*(1024*1024); // how many bytes per chunk 
 $buffer = ''; 
 $handle = @fopen($filename, 'rb'); 
 if ($handle === false) { 
  return false; 
 } 
 while (!feof($handle)) { 
  $buffer = @fread($handle, $chunksize); 
  print $buffer; 
 } 
 return @fclose($handle); 
}

header("Content-type: application/octet-stream");
ReadAndOutputFileChunked('/private/path/to/upload/files/' . $nameOfFile);
micahwittman