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);
Related
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.
Im working on an upload script, and i want a user to be able to upload any file.
I had it al working on localhost, i added
php_flag engine off
AddType text/plain php html shtml php5 php4 php3 cgi asp aspx xml
to my htaccess in the upload folder, and it showed the source of PHP, html and all other files. Exactly as i wanted to.
Now i tried to upload it to a real webserver, and unfortunately my host does not allow such .htaccess files.
I tried openinging the files with file_get_content() and fopen() and giving them a text/plain header.. but nothing works. It first executes the scripts and shows the output in my textarea.
Do you guys have any suggestions on how i can fix this without .htaccess ?
Thanks!
Don't upload files into the webroot and let people access them directly. As you say, .php scripts (and probably a lot more) get executed that way. A classic way for arbitrary code execution attacks.
Store uploaded files outside the webroot where they're not publicly accessible and create a script that allows users to download the files, for example using readfile or Apache mod_xsendfile, after having done the necessary permission checks.
Also see Security threats with uploads.
previously I could download wav file in php and IIS. But now file is not downloadable.I don't know what is going wrong . After installing and changeing php 5.3 to php 5.4 with php manager in IIS, the file is not able to downloaded. I have link to file to download it which looks like this:
Download
download.php scripts
<?php
$filename = $_GET['voice'];
$dir = 'd:/temp_file/voice/';
if(is_file($dir.$filename))
{
header('Content-type: audio/wav');
header('Content-Disposition: attachment; filename='".$filename.'"');
echo file_get_contents($dir.$filename);
}
?>
while I was trying to find the mistake why file is not downloadable, I remove if statement and run program, it does prompt window download file with audio player of empty wav file. So, I conclude that there is mistake in path which is not allowing to access file of another drive. I have php code in c:\inetpub\wwwroot\ but wav file to be download is in the d:\temp_file\voice path. What Should I have to do?
Maybe PHP open_basedir restriction.
Try to add d:/temp_file/voice/, to its content in php.ini.
Update
I never used IIS, but another thing that you have to check is if the IIS User have permission to read that directory. Try to add, just for test, Everyone with all permission to d:\temp_file\voice.
I don't know what is going wrong
Then enable error reporting, for what it looks like to me it's a syntax error at header('Content-Disposition: attachment; filename='".$filename.'"'), you're missing a quote (') after filename='". But since you say it has worked (and works without the if), I'd say that's a copy paste error.
Then do a var_dump($filename), which might contain quotes, since you put those around the URL in the link (<a href="download.php?voice='$filename'">). The file D:/temp_file/voice/'foo.wav' might not exist.
Just a quick one - I wrote a php script recently that dynamically creates XML file using API DOM. So I'm using this at the beginning:
$dom = new DOMDocument('1.0', 'UTF-8');
And at the end it looks like this:
$server = $_SERVER['DOCUMENT_ROOT'];
$path_to_xml = "$server/project/file.xml";
file_put_contents($path_to_xml, $dom->saveXML());
It does everything I wanted but why browser is trying to download this php script instead of just run it? Please can someone help me with this. I'm pretty sure it's something easy.
//-----------------------------------edited
Thanks for all replies. Yes I'm sending custom headers because it's google maps kml file that I'm creating dynamically.
header('Content-type: application/vnd.google-earth.kml');
// Creates the root KML element and appends it to the root document.
$node = $dom->createElementNS('http://earth.google.com/kml/2.0', 'kml');
$parNode = $dom->appendChild($node);
Could that be possible cause of this?
Because your web server is not correctly configured to process PHP scripts.
I have experienced that it happens due to some error in the .htaccess, it may seem absurd because i did not mentioned anything to see in the .htaccess. You should take a closer look in to it.
If the browser is trying to download the PHP source, then this means that Apache was either not configured to run the PHP interpreter and/or, if you are using a Linux, Unix, or Mac OS X operating system, Apache did not have permission to execute the PHP script.
You need to follow the instructions at Manually Configure PHP5 and Apache2 to make sure that your httpd.conf is correct.
If, in addition, you are running Apache on Linux, Unix, or Mac OS X, then you need to open a terminal, cd into the directory that contains your PHP script, and:
chmod a+x SCRIPT.php
where SCRIPT.php is the name of your PHP script.
I'm assuming that this command sends a header:
header('Content-type: application/vnd.google-earth.kml');
If you're writing a file on the server and not intending to send any response to the client, why are you sending a header to the client?
If your PHP file is just supposed to write a file on the server and do nothing else, don't send a header or indeed anything else to the client.
If that doesn't help, try rephrasing your question. You have received a wide variety of responses so far to all manner of different problems.
If the file extension is .php and your web server correctly configured it will run it.
You specify an application/xxx content type so most browsers will force a download and use the name of your script as file name.
If you want to force a file name different from your php file name use :
header('Content-Disposition: attachment; filename=your_requested_file.kml');
You might not be sending correct headers:
<?php header ("content-type: text/xml");
My guess is that the browser is not trying to download the PHP script, but IS trying to download the KML file. Comment out the header() line and see if it works. When saving a file locally, you should not need to include header().
On the current website I'm working on, I've got a directory of files for users to download which would be really nice to have some security method other than obscurity ;)
I was wondering if there's any way to supply login information via PHP to htaccess as though a user were entering it.
Alternately, if anyone knows a better way to secure user downloads using PHP, that's also acceptable. All of my googling turns up "just use htaccess" which isn't really helpful, as from the non-savvy user's point of view, they have to log in twice every time they use the website.
My best guess at doing it exclusively with PHP is to store files above the web root, then copy them to a web accessible folder temporarily, but this seems highly inefficient and I couldn't think up any way to remove them after the download has finished.
Note: I don't own the server this is running on and don't have ssh access to it.
If files are not too big (Gb) you can always use readfile for file's download. In this mode you can check user's auth before, and if it's ok output file contents to user, otherwise send him to login page.
With this method you can put your files in protected (with .htaccess) directory so you can be sure that nobody who isn't authenticated can access them.
I think I would either store them in a folder outside of the web root, or in a folder protected by .htaccess and then have a php script that checked if the user was logged in and allowed to download a file asked for. If he was, then just pass the file through to the user.
Example from linked page at php.net:
Example #1 Using fpassthru() with binary files
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
Someone else made a comment about having to report the correct content-type, which is true. Often, in my own experience, I already know it, or can use the file extension pretty easily. Otherwise you can always try to have a look at finfo_file. On that page there are also some comments about what you could do especially for images as well.
you should use a php script to control the access.
create a dir outside the webroot or inside the webroot with a .htaccess where you location the download files.
outsite the webroot is better.
you have to make sure that no one can access those files if they are located inside.
then take from the pear class lib. the class http_download.
using this class has many advantages.
Ranges (partial downloads and resuming)
Basic caching capabilities
Basic throttling mechanism
On-the-fly gzip-compression
Delivery of on-the-fly generated archives through Archive_Tar and Archive_Zip
Sending of PgSQL LOBs without the need to read all data in prior to sending
you should not use readfile oder any forwarding filepointer because you have to set the headers yourself and the don't support http "range".
for the access restrictions you can use you session-manager, password, framework, forum etc.
pear - http_download http://pear.php.net/package/HTTP_Download
you need to copy the url, because SO encodes it to url-encoded string (which is correct), but PEAR-homepage doesn't like that.
Why reinvent the wheel? Take a look at File Thingy, which is pretty easy to install and customise. If nothing else, you can study the source to learn how to perform the authentication step.
You could use MySQL to store uploaded files, rather than storing them in a file, better and more secure, in my opinion. Just Google "MySQL upload php" for example.
You could create the htaccess file using a PHP script, from your users table, each time a user accesses that folder, very troublesome.
I think the first option is better.
Use X-SendFile! There's extensions for Apache, Lighty and Nginx so there's a good chance there's one for your webserver.
Once you have the extension installed, you can authenticate the user using your PHP script, and then add the header:
header('X-SendFile','/path/to/file');
When your PHP script is done, it will trigger the webserver to stream the file for you. This is especially efficient if you use PHP along with for example FastCGI, because it frees up the PHP process for other work.
Evert