Nastary
Nastary

Reputation: 345

Save image file with php?

I have this code in my root to auto save image file when user click on link

<?
// Force download of image file specified in URL query string and which
// is in the same directory as this script:
if(!empty($_GET['file']))
{
  $filename = $_GET['file']; // don't accept other directories
  $size = @getimagesize($filename);
  $fp = @fopen($filename, "rb");
  if ($size && $fp)
  {
    header("Content-type: {$size['mime']}");
    header("Content-Length: " . filesize($filename));
    header("Content-Disposition: attachment; filename=$filename");
    header('Content-Transfer-Encoding: binary');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
    fpassthru($fp);
    exit;
  }
}
header("HTTP/1.0 404 Not Found");
?>

Questions:

  1. Can hackers send any string to this file and save my root files? (example: index.php or etc...)

  2. How I can check URL contain image file?

Upvotes: 2

Views: 239

Answers (3)

ljacqu
ljacqu

Reputation: 2180

This is the kind of thing where you need a whitelist approach. If all your images are stored in the same folder, just pass the file name (e.g. image.jpg) without any paths and disallow the occurrence of any /.

The check with is_scalar() below checks that someone doesn't do ?file[]=test, which will send an array to the server. This isn't a big deal sometimes but it can make the server output errors that we don't want.

if (!empty($_GET['file']) && is_scalar($_GET['file'])) {
    $filename = $_GET['file'];
    if (strpos('/', $filename) !== false) {
        exit;
    }

We can then look at the file extension and determine whether it is allowed or not. Notice the use of strtolower() so it matches uppercase extensions (PNG, JPG, etc.). The check $ext != $filename makes sure that no one is just passing "jpg" or "png" as file name.

    $allowed_types = array('png', 'jpg', 'jpeg', 'gif');
    $ext = preg_replace('/^(.*\.)(.*?)$/', '\\2', $filename);
    if (!in_array(strtolower($ext), $allowed_types) && $ext != $filename) {
        exit;
    }

Finally, check that the file actually exists and that it is indeed a file. file_exists() also matches directories, so we're being extra sure by check with is_file() as well.

    $filename = '/images/' . $filename;
    if (!file_exists($filename) || !is_file($filename)) {
        exit;
    }

After these checks, you are sure that $filename is OK and you can start doing things like the following (taken from your code):

$size = @getimagesize($filename);

Upvotes: 1

arunrc
arunrc

Reputation: 628

use exif_imagetype

int exif_imagetype ( string $filename )

Upvotes: 2

DannyTheDev
DannyTheDev

Reputation: 4173

Q1: You should restrict access to only specific directories - if that is just 1 directory then there is no need for you to use the directory provided from the $_GET request, simply include it in your php instead.

$filename = end( explode( '/', $_GET['file'] ) );
$filename = '/uploadsdirectory/' . $filename;

Q2: To check the extension of the file being requested you should do:

$extension = end( explode( '.', $filename ) );

this will return jpg or png etc.. bascially whatever is after the final period

Upvotes: 2

Related Questions