Had no luck finding an answer on the internet so I figured I'd ask the experts. What are the security threats that come with downloading files using url query, for example a code like this:
if(isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != ""){
$file = urldecode($_SERVER['QUERY_STRING']);
if(file_exists("files/".$file)){
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize("files/".$file));
ob_clean();
flush();
readfile("files/".$file);
exit;
}
}
would be vulnerable to query strings like "?..\..\..\any\file\here" but isn't that all? Can't this be filtered out by a single if?
$file = urldecode($_SERVER['QUERY_STRING']);
if(strpos($file, ".\\") !== false || strpos($file, "./") !== false){
echo "No you won't get my system files";
exit;
}
If there's no "upload" feature, a .htaccess file preventing access to \files\ directory and the "if strpos" then is this safe?
Edit: bad code example
1) You would be vulnerable to ..\..\..\any\file\here
2) I would recommend you to get a list of all sub-files in files/. Then if the file requested does not match any of those files return a 404 Error code.
PHP read sub-directories and loop through files how to?
Related
Here is my code. I wrote this to serve mp3, images and videos. Mp3 files are received and working well. Images and videos are received but corrupted. I'm new to php.
<?php
if( !empty( $_GET['type']|| $_GET['name']|| $_GET['ext'] ) ) {
// check if user is logged
if(true) {
$type = preg_replace( '#[^-\w]#', '', $_GET['type'] );
$name = $_GET['name'] ;
$file = "{$_SERVER['DOCUMENT_ROOT']}/cont/{$type}/{$name}";
echo $file;
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
}
}
die( "ERROR: invalid file or you don't have permissions to download it." );
Can you help me?
There is an echo $file; before the actual response that doesn't belong there.
Besides the text itself, PHP also produces a Warning on the first call to header(). Both these arrive in the final content before the file header and makes the data received by the browser unrecognizable by most file readers.
I have gone through all articles on Stack Overflow and can't fix my issue. I am using following code:
$file = $_GET['url'];
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
The above mention code is downloading the file from the directly above the root and Yes it is downloading a PDF file but the file is only of 1KB size and not the original size. The $_GET['url'] is receiving ../dir/dir/filename.pdf in it. the filename is space in it as well. For security reason I cannot share the file name.
Please let me know where am I going wrong.
Please make sure you are using the web server path to access the file - for instance your path could be: /home/yourusername/public/sitename/downloads/<filename>, you should check first - to help you can run this at the top of your PHP script to find out the full path for the current script:
echo '<pre>FILE PATH: '.print_r(__FILE__, true).'</pre>';
die();
Only send the filename with the url using urlencode() and on the receiving PHP script use urldecode() to handle any character encoding issues.
See here: http://php.net/manual/en/function.urlencode.php
and here: http://php.net/manual/en/function.urldecode.php
So where you create your url:
Download File
And in your php script:
$file_base_path = '/home/yourusername/public/sitename/downloads/';
$file = urldecode($_GET['url']);
$file = $file_base_path . $file;
$file = $_GET['url'];
if (file_exists($file))
{
if (FALSE!== ($handler = fopen($file, 'r')))
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: chunked'); //changed to chunked
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
//header('Content-Length: ' . filesize($file)); //Remove
//Send the content in chunks
while(false !== ($chunk = fread($handler,4096)))
{
echo $chunk;
}
}
exit;
}
echo "<h1>Content error</h1><p>The file does not exist!</p>";
I hope this helps you!
I am not able to open the file after the download.
It says the the file has been corrupted.
I guess i have used all the required headers fine.
In chrome it shows error like:
chrome resource interpreted as document but transferred with mime type application/octet-stream
In Firefox no error msg.
if (isset($_GET['file']) && basename($_GET['file']) == $_GET['file']) {
$filename = $_GET['file'];
} else {
$filename = NULL;
}
$err = 'Sorry, the file you are requesting is unavailable.';
if ($filename) {
// define the path to your download folder plus assign the file name
$path = '/wp-content/uploads/'. $filename;
// check that file exists and is readable
if (file_exists($path)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'. basename($path) . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
ob_end_clean();
flush();
readfile($path);
exit;
}
}
download: getting downloaded from ftp folder.
None of the formats are opening.
.txt is getting opened.
Let me know if i am in wrong direction.
Inserting into table:
echo "<tr><a href='?file=". $row["FileupName"]."'>".$row["FileupName"]."</td></tr>";
readfile('$path') is your issue, it should be readfile($path) (with no quotes)
In PHP, variables are only evaluated in strings if the string is defined in double quotes ". Effectively you're downloading a file where the contents is the literal string '$path', with an incorrect filesize.
In the website page contains many images with downloading options. If I click the download button it automatically downloaded on user system and it shows on browser downloadable page. I have PHP code like
$image = file_get_contents('http://website.com/images/logo.png');
file_put_contents('C:/Users/ASUS/Downloads/image.jpg', $image);
Above coding is working fine. But I need to provide the path name for image to save. In user side we don`t know the path.
I need the PHP code to use the browser download location and download images need to show the browser downloads.
not possible to store the image in particular user location due to security issues .you don't force user .you have to suggest him to store particular location .and also you don't know the what file system there in user system.and also downloading path can be setting up by user anywhere so your not able to get that.
$filename = '/images/'.basename($_POST['text']);
file_put_contents($filename, $content);
you have to save/download the image somewhere on your web serwer and next send the file to user using header function, for example:
$file = 'path_to_image';
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
else {
echo "file not exists";
}
manual
`<?php
$filename ='http://website.com/images/logo.png';
$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");
?>`
I am fairly new to php and have managed to string together this simple download script by reading some of the questions listed here at SO and would like to ask those of you that are more familiar with php to take a look at the code below and see if there are any glaring flaws with my implementation or anything that should be changed.
Just letting you know that the during my limited testing, everything seemed to work fine, but as I said I am fairly new to php and want to make sure I am not missing something that might break the script later down the road.
<?php
//Settings
$filesPath = './files';
$fileName = $_GET['file'];
$allowedExts = array('jpg','png','gif');
//Functions
//Returns the extension portion of a filename.
function file_extension($fileName)
{
$path_info = pathinfo($fileName);
return strtolower($path_info['extension']);
}
//Validation and processing
//Check that a file is actually being requested
if (empty($fileName)) {
die('no file was requested');
}
//Check that the file is allowed to be downloaded
if (!in_array(file_extension($fileName), $allowedExts)) {
die('you cannot download this file');
}
//Get the file
if (file_exists($filesPath . DIRECTORY_SEPARATOR . $fileName)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($fileName));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($fileName));
ob_clean();
flush();
readfile($fileName);
exit;
}
?>
TIA,
Dave
Your script may be vulnerable to directory traversal. In your case, I would use realpath() on the filename and check that it is a valid file within .files/.
There is a possibility that someone can traverse up the directory tree and steal files such as /etc/passwd and so on.
you are open to hackers attacks, someone could just do ?file=../etc/passwd/ and bam owned
you better do some checks on how to protect against lfi