Restrict file access to authorized php users - php

I've inherited an application with a glaring security hole.
It has session-based security, but file uploads (which are user specific) are not secured in any way and they are stored in the public file tree.
Filenames do not follow any convention as such, making them hard to guess, but the data is sensitive and thus I need to implement a security measure to prevent unauthorized file access.
Moving the location of the files is not really an option, so I'm looking at a htaccess solution to forward requests to a php handler script.
Does anyone have experience in implementing this type of thing or any good alternative solutions? Specific examples of .htaccess syntax greatly appreciated, as I'm struggling in this area.

Don't really understand why moving them isn't an option, since pushing requests for them to a handler means it no longer matters where they're stored. But you're the man on the scene.
.htaccess looks like:
RewriteEngine on
RewriteRule path/to/where/these/files/live/(.*) /handlerscript.php/$1
Then you pick up the remaining file path and name from $_SERVER['PATH_INFO'].

Well, you could make apache parse .jpg file's for a certain folder adding the following to your .htaccess
AddHandler php5-cgi .jpg
then you could set a file of php to parse the request the way chaos was recomending you and doing a certain validation, then just return jpeg headers along with the correct picture u'd like to display
here's an example
<?php
if($validUser)
{
header("Cache-control: No-cache");
header("Pragma: No-cache");
header("Content-Type: image/jpeg");
//correct picture address
$img = imagecreatefromjpeg("2326_b_lil.jpg");
imagejpeg($img);
}
else
{
//code for error image
}
?>
please let me know if you want a more extensive example

I think you may need to write a script that will serve the images, then use htaccess to completely restrict access to the actual images from a browser.
The script can take in the web path to the image, decide if the user has access, then use something like fpassthru to feed an actual image to the browser.
All references to the images would need to be modified, however, to reference the serving script.
So instead of accessing the images with /images/123/5423453245.jpg,
it would be /image.php?images/123/5423453245.jpg
Or something similar to that.

Related

Loading images from non-accessible directory with PHP - concerns about content type and method

I need to call and load images from a non-web accessible directory and am doing this with PHP.
Essentially I use:
<img src="https://example.com/fetch_image.php?image=image_name.png" />
The script itself looks like:
$displayimage = file_get_contents('/home/whatever/public_html/db/uploads/'.$_GET['image']);
header('Content-Type: image/jpeg');
echo $displayimage;
Now, this seems to work fine. But what I am unsure about is:
Is this the sensible way to do it?
What header should you use if you are calling a wide variety of image formats? Using image/jpeg doesn't seem to stop the loading of PNG etc?
Does the above approach create any security issues? I could in theory add a hashed and salted key to the URL parameters if needed (and then checked), but I didn't see the point.
Any help much appreciated.
Is this the sensible way to do it?
Yes, but not like this. One rule of thumb: When security is a concern, always consider the way you treat user inputs.
For example: https://example.com/fetch_image.php?image=../database.php would access the parent folder and "download" the contents of the database.php.
As long as your script is not taking care of any access rights / logging whatsoever, simply put your images into an accessible folder of your liking, e.g. assets.
What header should you use if you are calling a wide variety of image formats? Using image/jpeg doesn't seem to stop the loading of PNG etc?
For getting the proper MIME-type, you may want to take a look at finfo_file.

Allowing users to upload ANYTHING

I'm building a file sharing site, and I'm thinking, I want my users to be able to upload and share anything.
Sounds dangerous, I know. But, is there a method to allow this to be possible? For example, forcing the download when the user requests the link with a mime type? Rather than "running" something on the page.
Any ideas how to allow any file type without the security issue.
Thanks
Store the file on a location not accessible by the user through the browser. (so above the document root)
when loading a file, use the readfile() function.
Set correct headers, including these:
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=[INSERT FILENAME HERE]");
And also use the correct content-type header for each type of file
If you only want it to be dangerous to other users (as they will be sharing virus ridden files in no time), but provide some protection for your web server from code injection attacks, you might consider storing the uploads in a database BLOB field. That should at least make it harder to inject code that will run on the server.
You can allow users to upload anything by using a simple HTML multipart form along with PHP's $_FILES. Only issue I see is file size limits.
You than can simply post a link to any of these files. It's the user's browsers settings that determine if a file is executed or downloaded, not yours. So you can suggest that they right click on them, and select 'save to', but besides that it's limited.
Any ideas how to allow any filetype without the security issue.
You say it yourself. It's like wanting to make scrambled eggs, without wanting to break the eggs.

How to hide the source of a download on a webpage

I'm looking for a way to hide the source of my download. I'm surprised this is not more covered, but it also makes me wonder whether it's possible.
(Edit: By hide I mean make it difficult or impossible for end user to find a direct link to the file. They will thus be forced to actually be on the page, clicking it, for it to work.)
I found a script to force download files that are locally stored. The way I see it, it hides the true source (at least it's not in view source or download history).
http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/
So this works, I made it into a function that gets a linkID, and check that with a DB for the actual file-source. Hooray!
Only what if your downloads are on another server? Then you can't use most of the functions used here (like filesize, isreadable, fopen, ...). I'm not proficient enough to decide whether it is possible/feasible to make this work cross-server.
I realize that probably my webserver will lose bandwidth even though files aren't stored there, that's not a big issue.
Any info on the subject would be greatly appreciated. I prefer PHP, but I can work with whatever you give me, I really have no idea about this one.
You mean you want to hide the path of files stored on your server? If this is the case, simply store the files outside of your web root, and serve the files with a PHP script which will make use of readfile() + header() of appropriate headers depending on whether you are serving the file for opening or forced download. See http://php.net/readfile for plenty of examples on forced download scripts.
Sorry not possible. You HAVE to tell the browser where the resource is located so any savy user can simply decode the address or scan the HTTP request or their firewall logs or download history in the browser.
If you're trying to hide the path on your server then URL rewriting with mod_rewrite or aliases or other similar method should be sufficient.
UPDATE: Ok if using your own bandwidth is not an issue then all you need to be doing is outputting the files binary content to the browser and setting the relevant HTTP headers (ie, Content-Type and Content-Disposition). If the files MUST be stored remotely then you'll need your script to download and read them on-the-fly using CURL or similar before outputting the content.
If you mean hide the script or the directory the file is coming from, it's a simple answer--you can't.
BUT, you can make it only accessible on your terms, like using a script to render the file (as you have) but only when specific criteria are met. Alternatively you can move the file to a temp/secured directory and allow them direct access, but this also means waiting for the move, providing (what is deemed) reasonable and fair time to download the file, then removing it/deleting it when it's done.
You could use cURL to serve as a pass-thru for the content. This would conceal the source of the actual data, and allow you to secure it any way you choose. It would also take a lot of bandwidth, roughly 2 times the size of all downloaded files.
Give this a shot and let me know if it works?
if ($passesallyoursecurity) {
set_time_limit(0);
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: application/download");
header("Content-Disposition: filename=filetheyget.ext");
$ch = curl_init("http://remotedomain.com/dir/file.ext");
curl_exec($ch);
curl_close($ch);
exit();
}
I would recommend that you use a die() message without any HTML, whatsoever. in the document.
Then, insert the IP Addresses you want the webpage to decline. I would break the IP Addresses you don't like in an array(). Then use an "if" construct to see if any of those IP Addresses are lurking.
$decline_ips = array('ip_1' => '127.0.0.1');
if ($_SERVER['REMOTE_ADDR'] == $decline_ips['ip_1']) {
die("You aren't permitted direct access to this page.\n\n\n\n
Sources are blank.");
}
It works like a charm! And for XTRA-XTRA security, I would recommend inserting the IP address that isn't allowed (when they visit) to the Database so that whenever they try again, both the source is covered and the entire document.
But, you can just use the script I have posted on a different part of the document.

Good Way to Secure File Uploads in PHP

Writing a small app that (among other things) lets users upload a file (like an image, a .doc or a text file) as part of their posting/submission.
Our current prototype just dumps the file into /{app_root}/files/, but of course, anyone can get to that even if they are not logged in or using the system. The goal is to only grant access (view access) to the files if user is logged in and does in fact have access to the post that the file belongs to.
So, in short, I am looking for a good way to do this.
I am thinking of either creating a folder outside the /web/ (http) folder and then having PHP render it somehow using header() commans, or, maybe just dumping the file into the database? I have never done either one, however.
While I suspect I can figure it out eventually, there are just too many smart people on here that I was figuring someone will know of some sort of existing class or function library that does this already?
You have to do the following:
Move all the files out of the webroot. You could disable access to the folder with .htaccess, but it is not worth the hassle and potential security risk. Just move it out there.
Keep a table of the files uploaded, storing the user's original file name there. Rename the file to $id.$ext and so on. In short, you don't want to use the user's file name in your system.
Have a script, download.php or whatever, get the file's ID, verify who is logged in, and if everything checks out, fetch the file, read it out to the browser, and send the appropriate download headers.
These headers would be something like:
header('Content-type: application/octet-stream');
header('Content-disposition: attachment; filename=usersuppliedname.txt');
header("Content-Length: " . filesize('../safefiles/1.txt'));
header("Content-Transfer-Encoding: binary");
readfile('../safefiles/1.txt');
exit;
You can then get more fancy if you want to allow resuming files and such, but the above should do it.

How to password protect streaming videos with php

What is the best way to password protect quicktime streaming videos using php/.htaccess. They are being streamed using rtsp, but I can use other formats if necessary.
I know how to do authentication with php, but I'm not sure how to setup authentication so that will protect the streaming files urls so that a user can't just copy the url and share it.
Or am I overthinking this and I can just use a normal authentication scheme and place the files in a protected directory?
Both nginx and lighttpd web servers have X-Send-File headers you can return from PHP. So you can do your checks in PHP and then conditionally server out the file.
if (check_user_can_access()){
header('X-sendfile: /path/to/file');
} else {
header('HTTP/1.1 403 Fail!');
}
Lighttpd also has a neat module called mod_secure_download that allows you to programatically generate a URL that will only be valid for a short time period.
Nginx, and possibly lighttpd, allow you to cap the download speed, so you're not sending out streaming data faster than it can be consumed.
Either way, you want to use your web server for serving files. Serving them through PHP is possible, but slow.
Try to use Amazon S3 service, it got it's quirks but it makes sense once you get familiar with it.
There are hooks in their API to achieve temporally URL's that are active for specified time, so you can freely show url to visitor because it won't work 10 minutes or so later.
It's almost trivial thing to do with php (around 15 lines of code), there are a lot of examples on their forums so you dont need to go from scratch and read full documentation on how to achieve this.
What kind of authorization you will do before generate and show links it's up to you.
You can also have it look like it's served from your domain like video.yourdomain.com instead of standard s3 URL's.
Last thing, it's cheap - we payed around 2 US$ for the month of testing and deployment when I uploaded 8 GB and downloaded it 3 times completely and initialized download for around 100 times. The person I was doing this for is so satisfied by price that he wants to move all of his downloadable media to s3.
Now, re reading everything I wrote it looks like commercial/spam but I'm so satisfied with service because I coded everything for audio files earlier, and it took days until everything worked just fine and this took couple of hours to implement (mostly getting familiar with service).
You might want to take a look at:
mod_xsendfile (for apache)
It enables you to internally redirect to a file.
So you could point your download link to checkCredentials.php
<?php
if ( isAuthorised($_POST['user'], $_POST['pass']) ) {
header("X-Sendfile: $somefile");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; file=\"$somefile\"");
exit(0);
} else {
show403('bad credentials');
}
?>
This module is also available for other webservers. If I remember correctly, the idea originally comes from lighttpd, but - as Josh states- is also available for nginx.
First off, it is very easy to spoof a referer. This information is stored in the user's browser, so a user can simply telnet into your server and provide his own referer which matches your domain.
A couple things you could try:
First, more secure, but still spoofable. mod_rewrite provides the ability to check cookies. What you could do is set a cookie when the user visits your website that contains some obscure data. Then, you could modify your RerwriteCond to something like this:
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_COOKIE} obscurename=obscurevalue [NC]
RewriteCond %{HTTP_REFERER} !^http://(www\.)?yourdomain.com/.*$ [NC]
RewriteRule \.(asx¦ASX)$ http://www.yourdomain.com/images/leech.gif [R,L]
Another, better technique would involve working with PHP and mime-types. I'm not sure to what extent this would support streaming content, but I assume it'll work. What you can do is have all your video links point to a .php file (the query string will determine which video has been selected). Then, when a user tries to visit this link, you do something like so:
<?php
// You could also check some sort of session variable
// that is set when the user visits another part of your
// site
if(!isLoggedIn()){
header("Location: errorPage.htm");
exit;
}else{
// Get the name of the file specified
$file = get_file_name($_GET['fileID']);
// Specify the proper mime-type for the data you're sending
// (this may have to change, depending on your situation)
header("Content-type: video/vnd.rn-realvideo");
// Read the file and output it to the browser
readfile($file);
}
?>
From what I read, most servers know which mime-types are streaming mime-types, so the browser should be able to figure out how to handle the streaming file properly.

Categories