Keeping some files private - php

I was trying to create a directory of private files that could only be accessed when a user logs in. To do this, I used a folder outside the web directory, and then php to access it, if allowed.
Here's an example:
function display_movie($file){
printf("`<video id='movie' width='960' height='416' controls='controls' onerror='fix()'>`<br/>
`<source src='movie.php?file=%s' type='video/ogg; codecs=\"theora, vorbis\"'>
</video>", rawurlencode($file)`);
}
This works great for images, but breaks the media player. Also, I've only tested this locally on a Linux machine.
Any ideas? Thanks.
This is in movie.php...
if(file_exists($fileDir . md5($file) . $ext)) {
$contents = file_get_contents($fileDir . md5($file) . $ext);
}
header('Content-type: video/ogg');
echo $contents;

What is with the <br /> in you're movie.php ?
Did you tryed to see if movie.php echoes the file contents ? ( view source on you're page , then copy the movie source src "movie.php?file=..." and paste it in you're browser , see if it get's the movie out ) .
Allso you need to move the header and echo inside the if statement , if that file doens't exist you can echo a different standard movie :
if(file_exists($fileDir . md5($file) . $ext)) {
$contents = file_get_contents($fileDir . md5($file) . $ext);
} else {
$contents = file_get_contents($fileDir . md5(MOVIE_NOT_FOUND));
}
header('Content-type: video/ogg');
echo $contents;
Where MOVIE_NOT_FOUND is a constant, movie you whant to display if the requested one is not found .
One other thing you could do is enter you're full uri to the movie php in you're source src ( like "http://localhost/some/uri/movie.php?file=..."
From where do you get $fileDir , $file and $ext in you're movie.php ?
Edit: should take care of the file_get_contents problem you have
$file = $fileDir . md5($file) . $ext;
header('Content-type: video/ogg');
//set aditional headers you may whant here
ob_clean();
flush();
if( file_exists($file) )
{
readfile($file);
} else {
readfile($fileDir . md5(MOVIE_NOT_FOUND));
}
exit;

Related

SVG from PHP script is not working in img src

I'm trying to open SVG file first in PHP and then return this data:
$file = dirname(__FILE__) . $_GET["file"] . ".svg";
if (!file_exists($file)) {
$file = dirname(__FILE__) . $_GET["file"] . ".png";
if (!file_exists($file)) {
throw new NotFoundHttpException();
} else
header('Content-Type: image/png');
} else
header('Content-Type: image/svg+xml');
$content = file_get_contents($file);
return $content;
And in HTML:
<img src="script.php?file=someimage">
Problem is that its not showing SVG images in the tag. It works, if I set script.php?file=someimage to the URL string of my browser, but not inside the tag. PNG works fine. If i set just
<img src="someimage.svg">
it also works perfect.
embed and object tags works, but I need img.
UPDATE:
The problem was in Yii2, I send headers wrong way. In some reason it works for PNG, but not for SVG.
It should be done like that:
Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
Yii::$app->response->headers->add('Content-Type', 'image/svg+xml');
Looks like you forgot the /. Try replacing these lines where appropriate:
$file = dirname(__FILE__) . '/' . $_GET["file"] . ".svg";
// ...
$file = dirname(__FILE__) '/' . $_GET["file"] . ".png";
dirname() gives back a string without a trailing slash, so it was trying to open /path/to/scriptdirsomefile.svg.
Security
Also, I noticed that your script can be made to do bad things, such as exposing arbitrary files on the server. You can prevent a lot of exploits by doing some basic sanitizing of $_GET['file'] before starting:
$_GET['file'] = preg_replace('#/#', '_', $_GET['file']);
Change return $content; to echo $content;
So if the image files are on the same directory, it will be:
<?php
$file = $_GET["file"] . ".svg";
if (!file_exists($file)) {
$file = $_GET["file"] . ".png";
if (!file_exists($file)) {
throw new NotFoundHttpException();
} else
header('Content-Type: image/png');
} else
header('Content-Type: image/svg+xml');
$content = file_get_contents($file);
echo $content;
?>
Working example #1 (showing svg):
http://www.createchhk.com/SOanswers/subc/index.php
Working example #2 (showing png):
http://www.createchhk.com/SOanswers/subc/index2.php

After download rename source file in php

I loop .jpg file from my uploads/document and show it in my page and then i click to download one by one file and after I download .jpg from source in folder uploads\document\ the real file from my folder will be rename to other name.
I try these:
<?php
foreach (glob('uploads/document/' . '/*.{jpg}', GLOB_BRACE) as $file) {
echo '<a class="snapchat" download="' . basename($file) . '" href="/uploads/document' . '/' . basename($file) . '">' . basename($file) .
'</a><br/>';
rename('/uploads/document' . '/' . basename($file) ,'Mynew' );
}
?>
Any solution for these?
First : you cannot rename the source file before download , so you need to rename it after download
You can do that by PHP header() Function all you need is another php script to handle the download and rename the file,
Like :
1- Main PHP
<?php
$dir = 'files/';//uploads/document/
foreach (glob("$dir*.{jpg}", GLOB_BRACE) as $file) {
echo '<a class="snapchat" download="'.basename($file).'" href="download.php?fn='.basename($file).'">'.basename($file).'</a><br/>';
}
?>
2 - download.php
<?php
$dir = 'files/';//uploads/document/
$file = $dir.$_GET['fn'];
if (file_exists($file))
{
$type = 'image/jpeg';
header('Content-Type:'.$type);
header('Content-Length: ' . filesize($file));
readfile($file);
rename($file, $dir."mynewName");
exit();
}
else // file not exists Or has been downloaded before
{
header('HTTP/1.0 403 Forbidden');/* Retern Forbidden */
/*OR*/
//header("Location: 404.php?".$file); /* Redirect browser */
exit();
}
?>

Increasing security on imageUpload, issues with img src="image.php"

Cant seem to get the php script to execute and fetch the image script, the image placeholder.jpg is sored outside of the public_html webroot, here is what i do
Show image .php file
<img src="image.php?image=<?php echo urlencode('placeholder.jpg') ?>"/>
image.php
<?php
$file = basename(urldecode($_GET['image']));
$fileDir = '/noaccess/avatars/';
if (file_exists($fileDir . $file))
{
$contents = file_get_contents($fileDir . $file);
header('Content-type: image/jpg');
echo $contents;
}
?>
It is EXTREMELY unsecured to run that kind of script, because you are giving hackers access to your file system remember they can use ../ to navigate up the folders,
but anyway try this:
<?php
$file = basename(urldecode($_GET['image']));
$fileDir = $_SERVER['DOCUMENT_ROOT'] . 'noaccess/avatars/';
if (file_exists($fileDir . $file))
{
$imageRes = imagecreatefromjpeg($fileDir . $file);
header('Content-Type: image/jpeg');
// Output the image
#imagepng($imageRes);
// Free up memory
#imagedestroy($imageRes);
die();
}
?>

Serving documents outside the web root folder.

I have a function called "viewDoc" which is supposed to go to a folder outside the web root and fetch a file for me. It seams to work ok with images (Jpgs etc) but with PDF's it just outputs a blank gray page as demonstrated here - http://www.tutorplanner.com/userimage/viewdoc/12787622467.pdf
Can anyone see what I'm doing wrong as I've been scratching my head over this for a day!
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
if (file_exists($fileDir . $file))
{
$contents = file_get_contents($fileDir . $file);
//print_r($contents);
header('Content-Type: ' . mime_content_type($fileDir . $file));
header('Content-Length: ' . filesize($fileDir . $file));
readfile($contents);
}
}
readfile is used with the parameter of a file name - NOT text.
Two examples that would work (file_get_contents):
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
$filePath = $fileDir . $file;
if (file_exists($filePath))
{
$contents = file_get_contents($filePath);
header('Content-Type: ' . mime_content_type($filePath));
header('Content-Length: ' . filesize($filePath));
echo $contents;
}
}
or (readfile):
public function viewDoc($doc) {
$path_parts = pathinfo($_SERVER['REQUEST_URI']);
$file = $doc;
$fileDir = '/var/uploads/';
$filePath = $fileDir . $file;
if (file_exists($filePath))
{
header('Content-Type: ' . mime_content_type($filePath));
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
}
}
I also added the $filePath variable for you, as there's no reason to concat the string multiple times.
Edit
As extra security, to Yazmat's comment, you can use $file = str_replace(array('..', '/'), '', $doc); as this would remove all references to other directories (however, with the slash it would also remove access to sub directories, so you might want to skip that, dependend on your code and file structure).
You have a great security issue here, anyone can access anything on your server with the function you wrote. I realy advise you to not use it and just put your files (that should be accessible) on a public web directory.

PHP Readfile() not working for me and I don't know why

I am trying to get this code to work but for some reason, all the echo's are able to output correct content, but the headers don't seem to want to force the download of my document. What follows is the file I am trying to build for file downloads. It is set to input code like this: downloader.php?f=13&t=doc to download a file that is named 201-xxx.doc or 201-xxx.pdf from one of two folders depending on the users privileges.
All the logic works up to the header info at the bottom. If I comment out the header content type and the header content disposition, then it will read the file into the browser. With either of those lines included, it give me an error that says "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
<?php
//ob_start();
if ( !defined('__DIR__') ) define('__DIR__', dirname(__FILE__));
define( "TLOJ_FSROOT", __DIR__ . "/" );
define('WP_USE_THEMES', false);
require('./wp-blog-header.php');
$lessonnumber = $_REQUEST['f'];
$type = $_REQUEST['t'];
if ( $lessonnumber < '10' ) { $threedigitlesson = '00' . $lessonnumber; }
elseif ( $lessonnumber < '100' ) { $threedigitlesson = '0' . $lessonnumber; }
else { $threedigitlesson = $lessonnumber; }
$filenamestart = "201-" . $threedigitlesson;
$contenttype = 'application/octet-stream';
switch ($type) {
case 'pdf':
$extension = '.' . $type;
$contenttype = 'application/pdf';
break;
case 'doc':
$extension = '.' . $type;
$contenttype = 'application/msword';
break;
default:
$contenttype = '';
exit("It appears that you are trying to download a file that is not a lesson document. Please contact us if you believe this to be an error.");
}
$filename = $filenamestart . '.' . $type;
$current_user = wp_get_current_user();
//$siteurl = site_url();
$pathroot = TLOJ_FSROOT;
$download_path = $pathroot . "1hoefl4priaspoafr/";
if ( current_user_can("access_s2member_ccap_extendedlessons")) {
$download_path = $download_path . "ex/";
} else {
$download_path = $download_path . "st/";
}
$file_path = $download_path . $filename;
$tlojmemberlength = tlojunlocklessons();
if ( !is_user_logged_in() ) { exit("Please log in to access the file"); }
if ( !current_user_can("access_s2member_ccap_downloadlessons") ) { exit("You don't have access to download the lessons!"); }
if ( $lessonnumber > $tlojmemberlength ) { exit("It appears you are trying to jump ahead! While I am excited at your enthusiam, let's not rush our study time."); }
if ( ($lessonnumber > '195') && (!current_user_can("access_s2member_ccap_lastweek")) ) { exit("Upgrade now to access the downloads for the five bonus lessons!"); }
// build Final File Name
$extendedmessage = "";
if ( current_user_can("access_s2member_ccap_extendedlessons")) { $extendedmessage = " - Extended"; }
$myfinishedlessonname = "Lesson " . $lessonnumber . $extendedmessage . " -- The Life of Jesus Study" . "." . $type;
// echo 'Download Path: ' . $download_path . '<br />';
// echo 'Source/Lesson Number: ' . $lessonnumber . '<br />';
// echo 'File Name: ' . $filename . '<br />';
// echo 'File Type: ' . $type . '<br />';
// echo 'Allowed Lessons: ' . $tlojmemberlength . '<br />';
// echo 'Final File Name: ' . $myfinishedlessonname . '<br />';
// echo 'File Path: ' . $file_path . '<br />';
// echo 'Content Type: ' . $contenttype . '<br />';
// echo 'File Size: ' . filesize($file_path) . '<br />';
if (headers_sent()) { exit("Sorry but the headers have already been sent."); }
ob_end_clean();
if (file_exists($file_path)) {
header('Content-Description: File Transfer');
header('Content-type: ' . $contenttype);
header('Content-disposition: attachment; filename="' . $myfinishedlessonname . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: ');
header('Pragma: ');
header('Content-Length: ' . filesize($file_path));
flush();
ob_clean();
readfile($file_path);
exit;
} else { exit("No file present."); }
?>
Please help as I have been at this all day and am confused to no end why this won't work. Filesize() pulls the correct length so I know there is a file in the path that I am looking at. (I am also new to PHP, so if there is something that I am missing, please share.)
Thanks in advance!
If it's a big file, it cannot be sent with readfile. Try to use this:
$handle = fopen($file_path, 'rb');
$buffer = '';
while (!feof($handle)) {
$buffer = fread($handle, 4096);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
I am not sure why this worked, but I was able to solve this issue by breaking my php file into two pieces. Piece 1 loads WordPress and performs the logic validation. Then file 1 passes the information over to file 2 to do the download logic and write the header information.
Like I said, I am not sure why this worked, however a friend who knows PHP better than I do said that sometimes if the script takes too long to process then the headers won't take. It is possible that WordPress was hanging the script too long for these headers.
Hopefully this explanation will help someone else who is having this difficulty.
If you are trying to force browser to download the file with the use of
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="your-file.jpg"
but Chrome gives you ERR_FILE_NOT_FOUND and Firefox also fails with "Not found" (strangely Opera seems to work) try adding:
header('HTTP/1.0 200 OK', true, 200);
Chrome was telling me: "Error 6 (net::ERR_FILE_NOT_FOUND): The file or directory could not be found."
And Firefox as saying the file did not exist.
Although the same php file was handling a different type of download I was having issues with PNG and ICO, I tried some methods that only displayed the picture but did not prompt for a download box.
Finally I found out thanks to Crazycoolcam, that Wordpress was the issue.
I was including a php to a file I had called "tools.php",
inside of tools.php it had an include to wordpress's main header file,
to remedy the issue I split my tools file into a wordpress version and a non wordpress version and included the wordpress half after it had written the file out.
Just another possibility here as to why its not working. This was the cause for me. Interestingly, file_exists was returning true but no form of serving the file to the public for download was working without having the below set correctly.
PHP has a setting called open_basedir
Make sure this is set correctly relevant to your hosting environment. open_basedir can be edited via php.ini

Categories