Allow users to view or download files, but not execute them - php

I'm making a website that allows users to upload any kind of files. All the files are stored in a 'files/' folder. I want users to be able to see and download any of the files in that folder, but not execute them.
Some files, like JPG files, are easy: browsers have a built in system to display them, as we all know.
Other files, like PDF files, cannot be displayed directly, so what browsers do is prompt the user a download screen. Thats fine too.
However, other files, like PHP files, pose a problem. When I open one such file, if I am not careful, it gets executed. For example, if the file contains
<?php echo 'ok'; ?>
then when I go to 'files/myphpfile.php', what I see is just 'ok', and not the contents of the file. This will happen not only with PHP files, I presume, but also with any other file types that my server can execute (for example ASP files, I suppose).
What I need is to prevent users from executing the files, so that when they go to 'files/myphpfile.php', they either see the file contents, or the browser prompts them the download screen. Ideally it would be a general solution, that would not require me to construct a list of every file type executable by the server.
To me, the most reasonable approach was to set the file permissions to 644, so that users could only read the files. But after doing so, the PHP files keep getting executed.

To download:
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename=somefile.php');
header('Pragma: no-cache');
readfile('/path/to/somefile.php');
To display:
echo htmlspecialchars(file_get_contents('/path/to/somefile.php'));
UPDATE
When I open one such file, if I am not careful, it gets executed
That's why you don't have a direct link to the file. I.e. the files should not get uploaded to a location somewhere inside the document root. If the files are uploaded to a directory outside the document root people cannot access it directly by URL, e.g.: http://example.com/files/somefile.php.
This will happen not only with PHP files, I presume, but also with any other file types that my server can execute (for example ASP files, I suppose).
You are the only one who can tell what files may be dangerous. We cannot know whether you have some server which also serves (and parses) asp files nor what other potential dangerous files may be on there.
What I need is to prevent users from executing the files, so that when they go to 'files/myphpfile.php', they either see the file contents, or the browser prompts them the download screen.
See my already provided solutions above.
To me, the most reasonable approach was to set the file permissions to 644, so that users could only read the files. But after doing so, the PHP files keep getting executed.
This is because PHP files are not getting executed. They are only being parsed (i.e. being read by the webserver).
So basically you need to find out what are the possible dangerous file types (also think about other files that may get parsed by PHP e.g. .phtml or what not). Users shouldn´t be able to call executables anyway through a HTTP request. Because when I would do http://example.com/run-virus.exe it never gets executed on the server, but it only requests the file.

I solved the problem by displaying the files through a script, and not directly from their real location. The script was taken from an answer to a previous question of mine.
$fileinfo = finfo_open( FILEINFO_MIME_TYPE );
header( 'Content-Type: ' . finfo_file( $fileinfo, 'files/' . $filename ) );
finfo_close( $fileinfo );
readfile( 'files/' . $filename );
So now users that want to see 'files/myfile.php' must go to 'open/myfile.php', and the script above will prompt them the download screen, or display the file in the browser, if the browser is able to do so.

Related

How to only allow PHP to download images from my server?

I have a server that contains a simple php file for downloading images and a folder containing those images.
<?php
$filepath = "myFiles/" . $_POST["file"];
if (file_exists($filepath)) {
$file = fopen($filepath,"r") or die();
echo fread($file,filesize($filepath));
fclose($file);
}
?>
This download.php file as well as the myFiles folder are both located in the www/html/ folder.
I am trying to figure out a way to make it so that my PHP script can access my image files, while keeping the files locked away from regular visitors. My problem is that if I set permissions that the files can't be viewed through the browser, then the PHP script can't access them either. So either both have access or neither does.
Am I on the correct track? How could I make it so that I can download my images using a PHP script while keeping the images otherwise inaccessible?
That won't be something you can handle using the linux file system permissions. You can put back the linux permissions to what they were initially for the files.
Instead, if you have a /home folder, I would recommend putting the original files to hide there. Check with your webhost if you have one.
Otherwise, if you have to put everything in www absolutely, then put the files to hide in a new subfolder, e.g. "hidden-files", and in that folder put a .htaccess file inside to block direct browser access to the files. The .htaccess file can be a one-line file with Deny From All command inside.
This way your files will only be able to be proxied through download.php.

Deny access to folder's content if not logged in

Here is the current scenario:
I have an index.php file located at ./dl/ that lists the files in a folder named ./dl-meta/, using the glob function. It works pretty well.
The thing is, I would also like to password protect my files. I took a look at Apache's folder protect utilities, tho upon further inspection I realized that someone could easily brute force their way into my files. (I also saw that the password menu looked quite bad). So, instead of using Apache's rules, I tried making my own folder protect script in PHP, using session variables.
...The login script I crafted does a great job protecting my PHP files. Once the session is started, I can simply call this bit of code at the start of my PHP pages located at /dl-meta/ to see if the user has the right to view the page:
if($_SESSION['login'] === false){
echo "Access denied :(";
die();
}
Obviously, I can't run this piece of code for files like video.mp4 (AKA I can't deny access to video.mp4 if the user is not logged in).
Is there some way I can deny access for non-PHP files when the user is not logged in?
or maybe...
Should I obfuscate the /dl-meta/ folder? If so, how could I achieve this without breaking my file indexer and /dl-meta/'s folder hierarchy? Keep in mind that there are multiple subfolders in there...
Thanks for the read, SO! Feel free to ask for my code if you think it can help.
I figured it out...
Using readfile(); I can make it so im able to download a specific file, from another location. Heres an example:
$path = "./files/video.mp4";
header('Content-Type: ' . mime_content_type($path)); //Tell the browser to interpret as an .mp4 file
header('Content-Disposition: inline; filename="'.basename($path).'"'); //Change name of file (cool if you want to add some kind of watermark to your filenames)
readfile("../dl-meta/".$path); //Distribute the damn thing
Using the above code will make the client download the file/play it like if you directly accessed the resource. AKA you can download files from /script/file.php without revealing the URL of the actual file.
My goal is accomplished: I can protect files like video.mp4 behind a hand made PHP login wall.
Tho, some questions still remain unanswered.. Will this code make the downloads slower/use more system resources than vanilla direct downloading?
Bigger 5gb 2 hour MP4 files dont even load anymore... Any tips with that please?

How to safely upload and download PDF files stored in the file system using PHP

I've tried to look around for some tricks on how I can do this safely, without executing the code.
Does the code get executed if i simply upload it to the file system, then leave it be until someone downloads it?
Or is this a potential threat aswell?
What I am trying to do is making the users able to upload their CV in pdf files. The administrator can then download this pdf file (not being viewed on server, but downloaded).
How should I do this to prevent malicious files from being executed on my server? Also, would it be risky to place this folder OUTSIDE the public_html folder?
There's not a lot of risk to upload .pdf in a folder.
the folder must be in 644 (chmod). and have a index.php with redirect to the index of the website
Inside or outsite the "public_html" .. it's not a problem
when you upload, you can check the extension (.pdf) and type mime:
with finfo_file (http://php.net/manual/en/function.finfo-file.php )
and
with $extension = substr($file, -3);
That depends on how your server is set. If it allows PDF files to be used as executable, or to be opened with particular application/processing script that could be used for malicious actions. Otherwise you have to follow simple instructions such as restriction of file name length and avoiding to perform read operation of that file. As I understand you need only to upload and safe them. I'd keep them in public_html and once uploaded correctly (you checked filesize, extension etc) moved them whenever you like.

Convert the uploaded files to specific file format which can not download

I have a problem regarding to prevent download and saving of uploaded files.
My users can upload multiple files types like doc, pdf, ppt,etc....
This all file types are easily download if any one have url.
So what is the better way to prevent the download of the file.
Or i convert the uploaded files to some specific format which can not download easily (e.g flash)..
I am running on php and mysql.
Thanks
Avinash
You have two options in this regard. The first is to move the files, through a PHP script, to a server-side folder outside of the server's web directory. The second is to store the files in a BLOB column in a MySQL table. Both will prevent users from accessing the files directly, without the need to convert the file to a not-so-easily-downloaded format.
Upload the files outside of your document root. For example:
/var/username/uploads/file.docx
where your document root is
/var/username/public_html/index.php
So they can't be accessed directly. And then if you want to allow downloads, create a PHP file called "download.php" that does something similar to:
$data = file_get_contents('/var/username/uploads/file.docx');
header('Content-Type: application/docx');
header('Content-Length: '.strlen($data));
header('X-Content-Type-Options: nosniff');
echo $data;
and obviously you can add checks to see if the user has the proper permissions to download this particular file or is logged in.
A solution can be to set a user and a password to the upload folder, so only the users that know authentification details can download files.
Check next link for learn how to make htpasswd files on your server folders:
http://httpd.apache.org/docs/1.3/programs/htpasswd.html

Download PHP script instead of executing it

I have a downloads directory on a website where I store a bunch of different files for people to download (zip, exe, java, php, etc). The problem is that my website is written in PHP, so the web server, Apache, tries to execute the scripts instead of letting people download them. Without having access to Apache config (I'm on shared hosting), what is the easiest way to prevent Apache from executing scripts in a single directory?
I tried using mod_mime unsuccessfully. AddType doesn't work because (I'm guessing) a MIME type is already associated with PHP scripts. ForceType doesn't work because I store different types of files in the directory. Are there any other options?
If you have sufficient permissions for that, putting the following line in a .htaccess file in the directory in which you don't want PHP script to be executed might do the trick :
php_flag engine off
(Just tested on my webserver, and I got the source of a PHP script -- which had not been executed)
You could write a separate PHP script which sends the right Content-type header and then uses readfile() to pass through the contents of the PHP file without PHP actually executing them (and since Apache already passed off the request to PHP, it no longer cares). Just make sure you restrict it to only serving things out of that directory.
I think the common solution to this is to give files the extension phps.
I think I have the solution for you. Check this out:
http://www.boutell.com/newfaq/creating/forcedownload.html
Basically, it says that you should have the following code in your php page:
<?php
header('Content-disposition: attachment; filename=whatever.php');
header('Content-type: text/html');
readfile('whatever.php');
?>
I made a sample here:
http://sotkra.com/test.php
This forces the 'download' file prompt where the file download is the whatever.php
Cheers
You should have a download gateway script, such as download.php. It should take a query string argument which lists the file that needs downloaded.
That argument should be matched against a pre-existing ARRAY of accessible files (big security point there).
Then use:
<?php
$file = trim(isset($_GET['file']) ? $_GET['file'] : '');
$allow = array(
'foo.php' => 'text/plain',
'foo.jpg' => 'image/jpeg',
);
if(! isset($allow[$file]))
die('File not found.');
header('Content-Type: ' . $allow[$file]);
readfile($file);

Categories