views:

468

answers:

3

The company I work for has recently been hit with many header injection and file upload exploits on the sites we host and while we have fixed the problem with respect to header injection attacks, we have yet to get the upload exploits under control.

I'm trying to set up a plug-and-play-type series of upload scripts to use in-house that a designer can copy into their site's structure, modify a few variables, and have a ready-to-go upload form on their site. We're looking to limit our exposure as much as possible (we've already shut down fopen and shell commands).

I've searched the site for the last hour and found many different answers dealing with specific methods that rely on outside sources. What do you all think is the best script-only solution that is specific enough to use as a reliable method of protection? Also, I'd like to keep the language limited to PHP or pseudo-code if possible.

Edit: I've found my answer (posted below) and, while it does make use of the shell command exec(), if you block script files from being uploaded (which this solution does very well), you won't run into any problems.

+1  A: 

Use and configure Hardened-PHP create a plain script using move_uploaded_file and the $_FILES superglobal. The simplest the script, the safest it will be (at least, as safe as the running PHP version itself)

Vinko Vrsalovic
+8  A: 

The best solution, IMHO, is to put the directory containing the uploaded files outside of the "web" environment and use a script to make them downloadable. In this way, even if somebody uploads a script it can not be executed by calling it from the browser and you don't have to check the type of the uploaded file.

andy.gurin
Yes, and placing that directory in a special noexec mounted partition is even better
Vinko Vrsalovic
Make the partition nosuid also.
Eric Hogue
I agree that this is probably the safest method but, for my particular situation the answer I've posted below is what works for me.
Stephen
A: 

After lots and lots (...and lots) of research, I believe I've found the most viable method of upload verification. The method does have its drawbacks but for the average designer--which is who I was designing the script for--it works great:

$filename = time() . '_' . $_FILES[$upload_label]['name'];
$tmp_name = $_FILES[$upload_label]['tmp_name'];

$extension = strtolower(substr(strrchr($filename, '.'), 1));

// A list of accepted filetypes ordered by extension and info pertinent returned from the server
$accepted_files = array(
    'jpg' => 'JPEG', 
    'gif' => 'GIF', 
    'png' => 'PNG', 
    'pdf' => 'PDF'
);

// Ask the server for info on the file
$info = exec("file " . escapeshellarg($tmp_name));

// Distill the info down to a usable string
$info = array_shift(explode(' ', substr($info, strpos($info, ' ') + 1)));

// Compare the returned info to a pre-defined list of allowed types
if ($info == $accepted_files[$extension]) {

    // Passed verification
    if (move_uploaded_file($tmp_name, "$upload_folder$filename")) {
        // Success
        chmod("$upload_folder$filename", 0755);
    } else {
     // Failure
    }
}

The drawbacks are that you can't allow any php script files to be uploaded (since exec() is in play) and this method can only be used on Apache or any other server with a 'file'-like command. Other than that, this works great

Stephen