Reputation: 345
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:
Can hackers send any string to this file and save my root files? (example: index.php or etc...)
How I can check URL contain image file?
Upvotes: 2
Views: 239
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
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