Deny access to folder's content if not logged in - php

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?

Related

Deny direct access to PDF files in php web folder

There is a fully running PHP site that can only be accessed when logged in with ID/password.
I have been asked to add a function to the site that uploads PDF files and show existing PDF files in the browser.
So I achieved this by using iframe to embed PDF files in the browser.
But the problem is, the PDF files shouldn't be directly accessible (shouldn't be opened when accessing www.aaa.com/pdf/test.pdf), and must only be shown in the site so that only authorized users can view the file.
So I tried a few things :
.htaccess : I'm not too familiar with PHP/HTML so searched some answers on stackoverflow and tried them without success.
such as
order deny,allow
deny from all
allow from 127.0.0.1
or
Require local
and so on. It obviously blocked direct access, but also didn't allow the PDF to be shown from the site (www.aaa.com/showPDF.php). Come to think of it, the above methods seem to only allow the files to be read when opened from the physical server.
Moving the PDF folder outside of webroot : I succeeded moving the upload folder outside of webroot and also uploading to it, but then iframe doesn't work since PHP can't find the PDF file. A workaround was to use headers and PHP readfile(), but I can't seem to modify the headers from where I'm from since the headers are already sent beforehand in files that I cannot modify.
I think I'm making this overly complicated, and I think an appropriate .htaccess file will simply do, but don't have much knowledge in this area. Would really appreciate help here, thanks!
Seems it was simple enough.
Order Deny,Allow
Deny from all
Allow from www.aaa.com
worked. Thank you for your suggestions!
=======================================
Edit : above didn't work.
In fact, I didn't have permission to access outside my webroot.
So I stored the pdf files inside a folder within my webroot and denied access from all by using :
Deny from all
in .htaccess.
After that, since iframe was denied by .htaccess, I tried to print the contents of the PDF by using file_get_contents(). But this required setting header information such as
$contents = file_get_contents($filePath);
header('Content-Type: ' . mime_content_type($filePath));
header('Content-Length: ' . filesize($filePath));
echo $contents;
But the php page that shows the contents of the PDF file (showPDF.php) already sent out headers during initializing, and I didn't have rights to modify that part.
So I made a fresh php file (pdfviewer.php), added session information, and added the above 4 lines. After that I iframed pdfviewer.php within showPDF.php and it finally worked.
I think this problem was REALLY specific to my current situation but hope someone finds it useful.
Also, thanks #CBroe since I realized I was wrong and got some hints!

Forbid access to files in a simple PHP login system

I wrote this VERY simple PHP login system:
<?php
session_start();
$error = '';
if (isset($_POST['username']) && isset($_POST['password']))
{
if ($_POST['username'] == 'user' && $_POST['password'] == 'pass')
{
$_SESSION['client'] = 'ok';
Header ("location: /kit/kit/index.php");
}
else
{
$error = 'Usuario o contraseña incorrectos.';
}
}
?>
Don´t worry about the vulnerability issues, it´s not protecting anything valuable.
In every .php page i add:
<?php
session_start();
if (!isset($_SESSION['client']) || $_SESSION['client'] != 'ok')
{
Header ("location: /kit/index.php");
die();
}
?>
This protects the .php sessions just fine.
The problem is that this doesn´t protect the files.
I mean if go directly to:
something/other/file.zip
it will download it wether you have loged in or not.
I hope the question is clear enough, if not, please ask!
To stop a user from seeing the directory, all you need to do is create an index page in that folder. Ex: index.htm, index.html, default.htm, default.html.
To stop a user from entering the folder (e.g. stop anyone from viewing http://www.yoursite.com/myFolder/), you may need to access some features of your web host. Some hosts allow you to password protect files or folders. You can also create an .htaccess file/folder
An htaccess file is a simple ASCII file, such as you would create through a text editor like NotePad or SimpleText. Many people seem to have some confusion over the naming convention for the file, so let me get that out of the way.
.htaccess is the file extension. It is not file.htaccess or somepage.htaccess, it is simply named .htaccess
Create the file
In order to create the file, open up a text editor and save an empty page as .htaccess (or type in one character, as some editors will not let you save an empty page). Chances are that your editor will append its default file extension to the name (ex: for Notepad it would call the file .htaccess.txt). You need to remove the .txt (or other) file extension in order to get yourself htaccessing--yes, I know that isn't a word, but it sounds keen, don't it? You can do this by right clicking on the file and renaming it by removing anything that doesn't say .htaccess. You can also rename it via telnet or your ftp program, and you should be familiar enough with one of those so as not to need explaining.
htaccess files must be uploaded as ASCII mode, not BINARY. This makes the file usable by the server, but prevents it from being read by a browser, which can seriously compromise your security. (For example, if you have password protected directories, if a browser can read the htaccess file, then they can get the location of the authentication file and then reverse engineer the list to get full access to any portion that you previously had protected. There are different ways to prevent this, one being to place all your authentication files above the root directory so that they are not www accessible, and the other is through an htaccess series of commands that prevents itself from being accessed by a browser, more on that later)
JUST INCASE stop users from downloading your file
store all things that are downloadable ourside your document root. which means before the public_html file.
EDIT: updated the section below to show graphical representation of folder structure
how do you access them then?
work
downloadableFiles
downloadables
- memberOnlyFile.zip
- welcomePackage.zip
- memberhshipVideoVideo.mov
photos
- photo1.jpeg
- photo2.jpeg
publi c_html
- index.htm
About
- about.html
- about.gif
LogIn
- login.htm
- loginScreen.htm
- loginFancyButton.gif
Now anything in the public_html folder the world can see through your website.
Anything outside your public_html folder, will not be visible directly to the world through your website by typing the file name into the address bar in their browser. so thats a good thing as we are going to save all our files that we dont want to give access to outside of the public_html folder.
Now say if you want a certain user to be able to download a file, say maybe a logged in user, you can still make the file downloadable by having a link to that file.
If we are at the login Page, to access the loginScreen webpage you just write down the hyperlink like so:
login screen
since that page is on the same folder. now if you want to allow a user to be able to download a file from the downloadable files folder which is outside the public_html folder since it is not in that folder it self youjust reference to it like so:
How would we get to that folder if we are in the login folder as we are viewing the loginScreen.htm page, you go one folder back so we end up being in the public_html folder. then we go another folder back so we are in the work folder.
so it would look like this so far.
../../ which means two folders back.
then to access the memberonlypath.zip we then need to go into the downloadableFiles folder then we need to get into the downloadable files and then we can link it to the file membersOnlyFile.zip which is the file we were lookng for before.
so the full link now becomes
download file
This way the user cannot access the file by simply typing it on the address bar but can download it if you reference it yourself like the above.
Hope this helps
PK
Store all files you don't want downloaded outside the DocumentRoot.
You need .htaccess to deny access to the folder.
Just have a php download script like: this one that will get the file below the public_html folder.
"Static" files are served by the webserver, not PHP, so authentication is handled differently. There are two easy ways around this:
Handle all authentication in the webserver, e.g. with HTTP basic/digest authentication. Apache 2.2 has a helpful introduction.
Serve the files with PHP, e.g. with foo.php/path/to/file if you have "pathinfo" enabled (according to the PHP docs you set AcceptPathInfo=ON in the server config somewhere) or foo.php?path=path/to/file, which is pretty terrible, but oh well.
There is a more enterprisey solution:
Write an authentication module for your download server which understands authentication cookies from the other site. Many big sites do this (adcdownload.apple.com comes to mind), partly so they can stick the downloads on a CDN but still have some sort of access control.
There is a lazy workaround:
Stick everything in an "unguessable" directory name (e.g. some random base64 chars). Make sure you can't list the parent directory (the easiest way is to create an empty "index.html" file).

PHP - Question about uploading & uploaded image file

I have read the following tutorial "Uploading Files To the Server Using PHP"
and have several questions related to the topics.
Q1> The tutorial mentions that
"Note that PHP must have write access
to $uploadDir or else the upload will
fail"
For me, I only allow the user to upload the file after the user has login to the website.
If we set that $uploadDir permission as 777, then everyone can have written permission to that folder. How to avoid this problems?
Also I am using WAMP as my testing bed, can I simulate the same case as a real web server?
Q2> In order to prevent Preventing direct access, the tutorial mentions:
"A better approach is to move the
upload directory away from your web
root. For example, the web root for
this site is:
/home/arman198/public_html/ to prevent
direct listing i can set the upload
directory to /home/arman198/upload/."
Now my problem is that how can I display the uploaded images on other website pages. Since, the upload is not accessible directly anymore? I need to display the uploaded image save personal headshot dynamically on other website page. Is it possible?
Thank you
It's a common problem.
All modern computers have a temporary files directory. On Linux/Unix it's /tmp, on Windows it's usually c:\temp. The OS install will have set permissions on that directory so that anyone can write files there but only privileged users can delete files that don't belong to them. This is where PHP will want to put an uploaded file; your application then has to move it elsewhere (this is the purpose of the move_uploaded_file() function). PHP under Windows may need upload_tmp_dir actually set in the php.ini file.
Once you have an uploaded file, you can shift it whereever you like, including to where the webserver can read it to serve it. The biggest problem with that it is awfully easy to put this directory inside your codebase. Don't do that. As soon as you do anything beyond editing the files inside the directory they are served from, it will be problematic. Trust me: I've dealt with a few times this in code I've inherited. It's easy to let your webserver load files from a location outside your codebase.
The other alternative is to produce a download script. That way the file need not be servable by the webserver at all. One disadvantage is that you don't get to leverage the web server's MIME translation, but then, that lets you control which types of image files are permitted.
For the second question, you can use a PHP script intead of direct access to the directory. Lets name it image.php. Lets assume that it can take a parameter id, like image.php?id=image_id. In that file you can get the id using superglobal array $_GET. Then you can search for images with that Id and just send it as response.
First one I'm not sure, but maybe play with .htaccess file.
And for the first question, try setting your permissions to 775. That should allow PHP to write the file to the directory without giving the general public write access.

Performance-oriented way to protect files on PHP level?

I am looking for some input on something I have been thinking about for a long time. It is a very general problem, maybe there are solutions out there I haven't thought of yet.
I have a PHP-based CMS.
For each page created in the CMS, the user can upload assets (Files to download, Images, etc.)
Those assets are stored in a directory, let's call it "/myproject/assets", on a per-page basis (1 subdirectory = 1 page, e.g. "/myproject/assets/page19283")
The user can "un-publish" (hide) pages in the CMS. When a page is hidden, and somebody tries to access it because they have memorized the URL or they come from Google or something, they get a "Not found" message.
However, the assets are still available. I want to protect those as well, so that when the user un-publishes a page, they can trust it is completely gone. (Very important on judicial troubles like court orders to take content down ... Things like that can happen).
The most obvious way is to store all assets in a secure directory (= not accessible by the web server), and use a PHP "front gate" that passes the files through after checking. When a project needs to be watertight this is the way I currently go, but I don't like it because the PHP interpreter runs for every tiny image, script, and stylesheet on the site. I would like have a faster way.
.htaccess protection (Deny from all or similar) is not perfect because the CMS is supposed to be portable and able to run in a shared environment. I would like it to even run on IIS and other web servers.
The best way I can think of right now is moving the particular page's asset directory to a secure location when it is un-published, and move it back when it's published. However, the admin user needs to be able to see the page even when it's un-published, so I would have to work around the fact that I have to serve those assets from the secure directory.
Can anybody think of a way that allows direct Apache access to the files (=no passing through a PHP script) but still controlling access using PHP? I can't.
I would also consider a simple .htaccess solution that is likely to run on most shared environments.
Anything sensitive should be stored in a secure area like you suggested.
if your website is located at /var/www/public_html
You put the assets outside the web accessible area in /var/www/assets
PHP can call for a download or you can feed the files through PHP depending on your need.
If you kept the HTML in the CMS DB, that would leave only non-sensitive images & CSS.
If you absolutely have to turn on and off all access to all materials, I think your best bet might be symlinks. Keep -everything- in a non-web-accessible area, and sym link each folder of assets into the web area. This way, if you need to lock people out completely, just remove the symlink rather than removing all files.
I don't like it, but it is the only thing I can think of that fits your crtieria.
I'd just prevent hotlinking of any non-HTML file, so all the "assets" stuff is accessible only from the HTML page. Removing (or protecting) the page just removes everything without having to mess up the whole file system.
Use X-Sendfile
The best and most efficient way is using X-Sendfile. However, before using X-Sendfile you will need to install and configure it on your webserver.
The method on how to do this will depend on the web server you are using, so look up instructions for your specific server. It should only be a few steps to implement. Once implemented don't forget to restart your web server.
Once X-Sendfile has been installed, your PHP script will simply need to check for a logged in user and then supply the file. A very simple example using Sessions can be seen below:
session_start();
if (empty($_SESSION['user_id'])){
exit;
}
$file = "/path/to/secret/file.zip";
$download_name = basename($file);
header("X-Sendfile: $file");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . $download_name . '"');
Important note:
If you are wanting to serve the file from another webpage such as an image src value you will need to make sure you sanitize your filename. You do not want anyone overriding your script and using ".." etc. to access any file on your system.
Therefore, if you have code that looks like this:
<img src="myscript.php?file=myfile.jpg">
Then you will want to do something like this:
session_start();
if (empty($_SESSION['user_id'])){
exit;
}
$file = preg_replace('/[^-a-zA-Z0-9_\.]/', '', $_GET['file']);
$download_name = basename($file);
header("X-Sendfile: $file");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . $download_name . '"');
EDIT: How about a hybrid for the administrative interface? In the ACP you could access via the PHP method to, basically, send all file requests to the PHP authing file, but for public, you can use HTTP AUTH/htaccess to determine the availability of the result. this gives you the performance on the public side, but the protection on the ACP side.
OLD MESSAGE:
.htaccess is compatible with most Apache and IIS<7 environments (using various ISAPI modules) when using mod_rewrite type operations. The only exception is IIS7 + the new Rewrite module which uses the web.config file. HOWEVER, I'd be willing to be that you could efficiently generate/alter the web.config file for this instance instead of using .htaccess.
Given that, you could set up redirects using the rewrite method and redirect to your custom 404 Page (that hopefully sends the proper 404 header). It is not 100% appropriate because the actual asset should be the one giving a 403 header, but... it works.
This is the route I would go unless you want to properly create HTTP AUTH setups for every server platform. Plus, if you do it right, you could make your system extendable to allow other types in the future by you or your users (including a php based option if they wanted to do it).
I'm assuming the 'page' is being generated by PHP and the 'assets' should not require PHP. (Let me know if I got that wrong.)
You can rename the assets folder. For example, rename '/myproject/assets/page19283' to '/myproject/assets/page19283-hidden'. This will break all old, memorized links. When you generate the page for admin users that can see it, you just write the urls using the new folder name. After all, you know whether the page is 'hidden' or not. The assets can be accessed directly if you know the 'secret' url.
For additional security, rename the folder with a bunch of random text and store that in your page table (wherever you store the hidden flag): '/myproject/assets/page19283-78dbf76B&76daz1920bfisd6g&dsag'. This will make it much harder to guess at the hidden url.
Just prepend or append a GUID to the page name in the database and the resource directory in the filesystem. The admin will still be able to view it from the admin interface because the link will be updated but the GUID effectively makes the page undiscoverable by an outside user or search engine.
Store your information in a directory outside the web root (i.e.: one directory outside of public_html or htdocs). Then, use the readfile operator in a php script to proxy the files out when requested. readfile(...) basically takes a single parameter--the path to a file--and prints the contents of that file.
This way, you can create a barrier where if a visitor requests information that's hidden behind the proxy, even if they "memorized" the URL, you can turn them down with a 404 or a 403.
I would go with implementing a gateway. You set a .htaccess file to the /assets/ URL pointing to a gateway.php script that will deny if both the credentials are not valid and this particular file is not published or show it.
I'm a little confused. Do you need to protect also the stylesheet files and images? Perhaps moving this folder is the best alternative.

htaccess Authentication with PHP

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

Categories