Restrict file access -- only read through PHP [closed] - php

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 6 years ago.
Improve this question
I am using a GoDaddy web hosting plan on a Windows platform. This was not my choice -- it has to do with a different part of the actual site using ASP.NET (also not my choice).
I have a SQL database with a bunch of entries with some non-sensitive customer information. The primary key on this is an AutoIncrement integer, and I have a series of PDF files that match up with each of those integers (e.g. 555.pdf, 7891.pdf, etc).
My goal is to restrict direct access to these files, I want users to have to go through a search and login process (PHP) first. Originally I planned to put the files above the PUBLIC_HTML folder, but GoDaddy refuses to give me root access without a dedicated server ($20 a month from them).
The next thing I looked into was HTACCESS. I was going to restrict access to the files to only PHP scripts by only allowing access to the Server's IP Address (or localhost/127.0.0.1). Unfortunately this doesn't work because GoDaddy does not run Apache on its Windows servers.
I could put the files into BLOBs in the database, but that gets really messy when I need to work with them quickly (plus I have had some trouble with that approach).
Any suggestions to restrict access to the files only to a PHP script (readfile())?

Since you can't put the files anywhere but in your public_html directory, you'll have to go for the feared/hated "security by obscurity" method
Create a randomly named sub-directory to store the files in: public_html/RANDOMGARBAGE
Make sure the directory is not browseable. Disable directory browsing (if you can), and put a default document (index.html?) in there as well, so even if browsing is on, you won't get the directory listing.
Don't store your files with guessable names. Instead of storing them with the database ID, store them with a salted+hashed name instead: $crypted_filename = sha1($real_filename . 'some hard-to-guess salt text'); (of course, make this more complex if you need to). Store the original filename in your database. So you end up with something like:
public_html/RANDOMGARBAGE/5bf1fd927dfb8679496a2e6cf00cbe50c1c87145
public_html/RANDOMGARBAGE/7ec1f0eb9119d48eb6a3176ca47380c6496304c8
Serve up the files via a PHP script - never link to the hashed filename directly
Download
which then does:
<?php
$fileID = (int)$_GET['fileID'];
$crypted_file = sha1($fileID . 'some hard-to-guess salt text');
$full_path = 'public_html/RANDOMGARBAGE/' . $crypted_file;
if (is_readable($full_path)) {
if(user_is_allowed_to_see_this_file()) {
/// send file to user with readfile()
header("Content-disposition: attachment; filename=$ORIGINAL_FILENAME");
readfile($full_path);
} else {
die("Permission denied");
}
} else {
/// handle problems here
die("Uh-oh. Can't find/read file");
}
This way the user will never see what your "s00per seekrit" filename is, they'll just see their browser hit ...php?fileID=37 and start a download of secret file.pdf
On top of this, you can occasionally rename the special sub-directory to something else on a regular basis, as well as change the salt text (which then requires you update all the hashed filenames with the new sha1 values).

You can simply hide them. It's security-through-obscurity, but it sounds like your best option if you can't either keep them out of the web-root, or find a way to tell the server not to serve them directly.
So stick them in some randomly-named directory:
asd8b8asd8327bh/123.pdf
asd8b8asd8327bh/124.pdf
asd8b8asd8327bh/125.pdf
...
Then write yourself a little PHP script that will send appropriate headers, and pass the file contents through.
for example:
<?PHP
//pdf.php
$id = $_GET['id'];
//make sure nobody is doing anything sneaky. is_numeric() might do the trick if the IDs are always integers.
if (!some_validation_passes($id)){
die();
}
<?php
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="'.$id.'.pdf"');
readfile('asd8b8asd8327bh'.$id.'pdf');
Now, the above is really no better than just serving the files directly (yet), since people can still increment the id parameter in the query string.
But you ought to be able to figure out how to handle authorization pretty easily.

Because PHP uses the web server user's permissions, there is no way to restrict access to the files without either:
Placing them outside the DOCROOT
Changing the web server configuration to disallow access to those files
Changing the file so it will be interpreted by the web server, thus hiding its contents
Putting them in a database counts as outside the DOCROOT. For the third option, you could make the PDFs PHP files, but honestly, that would be pretty convoluted.
I recommend you contact GoDaddy and see if they have some way to configure per-directory file permissions.

Make a folder web inaccessable via chmod. PHP will still be able to include/require whatever is on the server, but users will not be able to navigate to the files ever.
Example:
This is set to 770, IE User and Group can Read/Write/Execute, Other can do nothing.

Related

Make a second url link to the same directory as a different url, without copying the content to a second directory?

I have files in a directory, "w", and I want to have those same files accessible from a different directory.
I have a MediaWiki installation in the directory 'w', creating a short url to link to the url 'wiki'. I have the files in the 'w' directory but it can be accessed from http://example.com/wiki. I want to have a second wiki entirely with the url format of http://example.com/second-wiki.
Since MediaWiki uses the content of files from a database the code never actually needs to change, even the LocalSettings.php. I set up a database system, modified the MediaWiki system, and created multi wiki support in a single database, by using a database table with input information such as the url to use. Or even use the same files and add a localsettings.php file to a directory 'w2' but use everything else from the original directory, 'w'. Is this possible? Preferably using .htaccess, or some other equally easy to edit. I don't want any changes to php configuration though.
I believe this may help...... - it is a simple redirect done in PHP so it is easy to edit later, etc.
I'll work with the fact you have a 'w' directory accessed by http://example.com/wiki and you want to access that through http://example.com/second-wiki (where 'magic' will be done to actually open the other URL).
In the 'w2' folder, make an index.php file with the following contents:
<?php
header("Location: ../w");
// NOTE: you may need to make that w/index.php or other pointer
exit();
Now, any time you access http://example.com/second-wiki, you will actually see http://example.com/wiki
Simple and easy to change later if you need to!
Not sure from the question that this is the solution (it fits the topic and some of the question text, though the question text does 'jump around' a bit...) - if it isn't, please rephrase the question to be more clear on just what you are looking for (in one place you say "the code never actually needs to change, even the LocalSettings.php" - then in another you talk about needing separate settings.....) - however, if you are looking for the 'simplest' (IMHO) way to make multiple URLs point to the same folder, this is the way to go - pure PHP and easy to mod later!

PHP file changes its extension to ".suspeced"

I have two php files on server that keep changing their extension over night. For example, file.php changes to file.php.suspected
What these two files have in common is mail() function. For example,
$mail_success = mail($_POST["email_to"], stripslashes($_POST["email_subject"]), stripslashes($_POST["email_body"]), $_app["email_headers"]);
There is nothing else that these files have in common. So, something on the server goes through the files over night and apparently dislikes these files with mail() function, changing their extension or rather adding .suspected extension to .php extension.
After a couple minutes of googling, it looks like a PHP file changing filetypes is the sign of a hacked server. Here is a post on the CPanel forums, where a guy has a similar issue and the other commenters decide that his server had been hacked.
I personally can't give you any advice to secure your site, but perhaps you should head over to SysAdmin or another community and look into making your server more secure.
I'm about 3 years late, but...
I found a piece of WordPress malware that does exactly what you describe.
It's something of a cleaner - it has 56 different functions to decide is a given ".php" file name constitutes code that needs to be rendered inoperable.
One of those indicators is a substring of what you say the two renamed files have in common:
function ryypkex($content)
{
if (strpos($content, " = mail(\$_POST[") !== FALSE) {
return TRUE;
}
If a given filename's contents match a condition, that filename gets its ".php" suffix changed to ".php.suspect".
The two instances of this cleaner that my honey pot caught use the "RC" action of the WSO web shell to immediately execute the cleaner. No on-disk cleaner code will exist. The implication is that you probably need to look for a WSO web shell somewhere in your web apps' code.

Is it possible to use PHP glob() in another domain?

If I use blob with a relative path I would use something like this:
$files = glob("../uploads/*");
If I want to check if another website (but in the same hosting account) has the files, can I use this?:
$files = glob("http://myothersite.com/uploads/*");
I also want to know if it is possible to delete files in another domain, like the example above but using:
unlink("http://myotherwebsite.com/uploads/file.jpg");
EDIT:
Thinking about it, if I want to delete files in another domain, I would have to create a webservice or something with the proper validation so it knows the deletion is being requested by a safe source.
For example a php file that receives GET or POST parameters to know what file will be removed, then do the unlink() process. But that function, for obvious reasons, does not work from foreign apps.
First thing. I assume you talk about glob() function and not blob(). If so please correct your question.
If website is at the same hosting account, you probably may navigate to other paths in your account.
Let's assume you have files inside:
/home/yourname/site1
/home/yourname/site2
/home/yourname/site3
Then you can simple use glob('/home/yourname/site3'); when you run it at your site1 url. You simple don't need to use urls firm glob() and in fact you can't:
Note: This function will not work on remote files as the file to be examined must be accessible via the server's filesystem.

Is php fileinfo sufficient to prevent upload of malicious files?

I have searched around a bit, and have not really found a professional type response to how to have secure fileupload capability. So I wanted to get the opinion of some of the experts on this site. I am currently allowing upload of mp3s and images, and while I am pretty confident in preventing xss and injection attacks on my site, I am not really familiar with fileupload security. I basically just use php fileinfo and check an array of accepted filetypes against the filetype. For images, there is the getimagesize function and some additional checks. As far as storing them, I just have a folder within my directory, because I want the users to be able to use the files. If anyone could give me some tips I would really appreciate it.
I usually invoke ClamAV when accepting files that can be shared. With PHP, this is rather easily accomplished with php-clamav.
One of the last things you want to do is spread malware around the globe :)
If you can, do this in the background after a file is uploaded, but before making it public. A quirk with this class is that it can load the entire ClamAV virus definition database into memory, which will almost certainly stink if PHP is running under Apache conventionally (think on the order of +120 MB of memory per instance).
Using something like beanstalkd to scan uploads then update your DB to make them public is a very good way to work around this.
I mentioned this only because the other answers had not, in no way did I intend this to be a complete solution. See the other answers posted here, this is a step you should be finishing with. Always, always, always sanitize your input, make sure it's of the expected type, etc (did I mention that you should read the other answers too?)
"malicious" files are not the only way to hurt your server (and if your site is down, it hurts your users).
For example, a possibility to hurt a server would be to upload a lot of very small files :
it would not use all the space on the disk,
but could use all available inodes...
...And when there is no free inode left, it's not possible to create any file anymore ; which, obviously, is bad.
After that, there is also the problems like :
copyright
content that is not OK to you or your users (nudity ? )
For that, there's not much you an do with technical solutions -- but an "alert the moderator" feature is oftne helpful ;-)
No, because this could easily be spoofed. There's an article that describes how a server could be attacked by uploading a 1x1 "jpg file" and how to prevent it. Good read.
The first thing to do would be to disable execution of any server side code (e.g. PHP) in that directory via server configuration. Setting up a whitelist for MIME types (or file extensions, since your server uses those to figure out the mime type in the first place) and only allowing media files (not HTML or anything) will protect you from XSS injections. Those combined with a file type check should be quite sufficient - the only thing I can think of that might get through those are things that exploit image/audio decoders, and for spotting those you'd need something close to a virus scanner.
To start with the "file-type" ($_FILES['userfile']['type']) is completely meaningless. This is a variable in the HTTP post request that can be ANY VALUE the attacker wants. Remove this check ASAP.
getimagesize() Is an excellent way to verify that an image is real. Sounds files can be a bit more tricky, you could call file /tmp/temp_uploaded_file on the commandline.
By far the most important part of an uploaded file is the file's extension. If the file is a .php, then you just got hacked. It gets worse, Apache can be configured to ignore the first file extension if it doesn't recognize it, and then use the next extension, so this file would be executed a normal .php file: backdoor.php.junk. By default this should be disabled, but it was enabled by default a few years ago.
You MUST MUST MUST use a file extension White List. So you want to force using files like: jpg,jpeg,gif,png,mp3 and reject it otherwise.
if exiv2 can't remove the metadata its probably malicious or corrupted in some way atleast. following required exiv2 be installed on your unix system. Unfortunately, this might be dangerous if the file contains malicious shell code. not sure how sturdy exiv2 is against shell exploits, so use with caution. i haven't used it, but i've thought about using it.
function isFileMalicious($file)
{
try{
$out = [];
#exec('exiv2 rm '.escapeshellarg($file).' 2>&1',$out);
if(!empty($out)){
return false;
}
}
catch(exception $e)
{
return false;
}
return true;
}

Securing a PHP file manager from its users

Hello everyone, I'm developing a photo sharing web site using the CodeIgniter PHP framework. The idea is that people could upload their photos, manage them (through some sort of file browser which allows them to create subfolders, drag files around, etc) and edit them (some basic things like resizing, rotating and cropping to start with, and later on, I'll add some advanced features).
I've already implemented a third party authentication solution for CI (Redux Authentication 2 Beta) and I'm now integrating a JS/PHP file manager (AjaxExplorer), but the problem is that the PHP backend for managing files (moving, copying, etc) is trusting too much on the user input from the ajax calls. For instance, it's doing things like this (simplified for the sake of clarity):
move_uploaded_file($_FILES['upload']['tmp_name'], $root.$username.$_POST['destination_dir']);
As you can see, there are obvious security concerns as it blindly accepts whatever path the user throws in! I can already see someone sending something like "../AnotherUser/" as the $_POST['destination_dir'] value.
My question is: What's the best way to "sandbox" a user, in order to only allow him to manage his own data? Do I just validate+filter the inputs, hoping to catch every attempt of intrusion? Are there any libraries/packages dedicated to address this specific issue?
I think this problem must be somehow solved in any (mature enough) project, which gives its users the power of managing their files through a web browser, so I expected to find some clear guidelines around this (as there are a lot about SQL Injection, XSS, CSRF, etc) but I guess I'm not using the right keywords.
What's the best way to "sandbox" a user, in order to only allow him to manage his own data?
Allow any filenames/directory names the user wants, but simply don't use them on the server side filesystem. Instead, write the path names into a database with a primary key, and use the primary key as a filename like ‘34256.dat’ in a flat storage directory (or even as a BLOB in the database if you prefer). Then serve up via a download script or URL rewrite to make the desired filename appear in the URL.
Sanitising incoming filenames is hard. Detecting ‘..’ is only the beginning. Too-long filenames; too-short filenames; combinations of leading and trailing dots; combinations of leading and trailing whitespace; the different directory separators of different platforms; characters that are invalid on some platforms; control characters; Unicode characters and the environment-specific ways of addressing them; ADSs; filenames (‘.htaccess’) or extensions (‘.php’, ‘.cgi’) that might be ‘special’ to your web server; Windows's reserved filenames...
You can spend a lifetime tracking down funny little quirks of filepath rules on various platforms, or you can just forget it and use the database.
I'm not sure what your destination_dir looks like, but what I thought of was assigning directories keys, and then getting the directory based on that key. For example:
//$_POST['destination_dir'] = '4hg43h5g453j45b3';
*_query('SELECT dir FROM destinations WHERE key = ? LIMIT 1'); //etc.
However you have to predefine keys before hand. Another alternative could be the opposite: md5/sha1 the input and use that as the destination_dir, then store that key in the database with the associated label.
There are no library's that I know of.
However in your particular example, strip all (back)slashes and dots from the string and then append a slash to the end of it, that way the user can't change folders.
$destdir = str_replace(array('.', '/', '\\'), '', $_POST['destination_dir']);
$destdir .= "/";

Categories