Restricting files access

Permalink 1 user found helpful
Hey,

I want to restrict the access to certain files in the "files" folder. With an htaccess file, i'm intercepting all requests to the "files" folder to go through fileInterceptor.php. I need to see if it's from a user logged in, but I don't have access to the Loader object.

How can I initiate the Loader object?
I get this error: Fatal error: Class 'Loader' not found

Thanks!

slafleche
 
Blenderite replied on at Permalink Reply
Blenderite
Why can't you put these files in a set and only allow registered users to access that set?

-Blenderite
slafleche replied on at Permalink Reply
slafleche
Because there are 3 categories of files. When you put concrete's protection on the file, it protects it with one password. That doesn't work.

Another problem is, let's say you have a protected page. User A has access and shares the link to a file on that page. Anybody dans download that file with that URL.
SheldonB replied on at Permalink Reply
SheldonB
If you set up a second file-folder behind your public HTML with the file manager settings concrete can serve the files but there will be no direct URL to access for users
slafleche replied on at Permalink Reply
slafleche
I'm not sure what you mean. My file folder is in my public_html. Where's the second one supposed to go?
SheldonB replied on at Permalink Best Answer Reply 3 Attachments
SheldonB
so concrete5 lives in /public_html/concrete(whatever)

you can create an empty folder
/something
/public_html/concrete

/something is just a folder on your server not public

concrete can read your folder if you tell it to add a second storage location
It will then serve the link only if the user has a permissions
users can only see /xxx/yyy not /xxx/yyy/something.jpg

go to dashboard-settings-extensions-File Storage Locations
add the path /home/yourfolder

in the files manager if you click on the file you want to protect you can change the file location after uploading this then moves the file to the new folder using the concrete file structure and keeping concrete permissions *Im not sure if you can make an upload folder in that folder

if they share the link that user must have permissions to view the files

you can do the levels of 1,2,3 of different access with file sets but you need advanced permissions on.

johnthefish has this add-on that might help with users that need to upload he also has a user file manager that is a block
http://www.concrete5.org/marketplace/addons/front-end-file-uploader...

I think the document library add-on also helps with this
mhawke replied on at Permalink Reply
mhawke
Perhaps I'm confused about what you are trying to prevent. The links produced by the 'File' block respects the permissions on the file. You can even set the permissions on the file to 'File Uploader' and then only the user that uploaded the file in the first place can see it.
slafleche replied on at Permalink Reply
slafleche
The file block does respect the permissions set, but there are a few problems. It's true, you can protect the files on concrete, but the problem is it's an all or nothing protection. In concrete's File Permissions page, the "View Files" section doesn't let you assigne groups to file sets.

I also can't use the option that the files uploaded are only visible by the uploader, because only the admin will upload files. There are paid memberships and the members will have access to different amounts of files, dependent on the membership they chose.

Our client needs 3 groups. Level 1 is accessible to all. Level 2 files to level 2 members and level 3 members. Level 3 files to level members 3 only. I can't put a password either, since there are 3 groups and access to those files will expire after a year.

Even if the file block generates links that check if a user has access, it's possible for someone to access the file directly with the correct path in the files folder.

When you use the file block, the href generated is something like: /index.php/download_files/X/Y

If you click on the link generated by the file block, you get a path like this:http://[your_site]/files/X/Y/Z/[your_file]...
That link is unprotected. I can send it to anyone and they'll be able to download that file.

So the plan is to intercept all requests to the file folder, see who's asking for it and then decide if we give it to them or not. I'm just not sure which files i need to include to have access to the basic concrete classes.
fieldsyncmobile replied on at Permalink Reply
fieldsyncmobile
I see where you're going with this. I am uploading some company sensitive files to C5 and setting their View rights to Admins and a couple other groups.

Within C5 the rights on the files are upheld, but if the direct URL is typed in, nothing stops anybody from viewing the file.

The URL's are somewhat complex, but that doesn't mean somebody with ill-intent many not discover them.

I need to look for an answer. This is somewhat serious.

Thanks - Tim
slafleche replied on at Permalink Reply
slafleche
SheldonB's answer works. I was able to get all the functionalities I wanted out it. Everything inside your web root has an url, so the easiest way to fix that is to take the sensitive data in a folder at the same level as your root folder. That way the only way to access that file is through php.

You can give an alternate storage location for files you upload through concrete. After you upload a file, you can move it to that folder.

If you need anymore help let me know.
fieldsyncmobile replied on at Permalink Reply
fieldsyncmobile
Ah, good to know. I will look into that because the info is very sensitive and needs proper protection.

Thanks for the reply.
fieldsyncmobile replied on at Permalink Reply
fieldsyncmobile
I see where you're going with this. I am uploading some company sensitive files to C5 and setting their View rights to Admins and a couple other groups.

Within C5 the rights on the files are upheld, but if the direct URL is typed in, nothing stops anybody from viewing the file.

The URL's are somewhat complex, but that doesn't mean somebody with ill-intent many not discover them.

I need to look for an answer. This is somewhat serious.

Thanks - Tim
Sadu replied on at Permalink Reply
Sadu
I think I have found an answer that should help some people. Note my requirements are slightly different to the OP and others.

I want to protect any PDF and DOC files that are uploaded via the form block - think sensitive documents that visitors upload through a form block. Not worried about images.

The first step is to assign all PDF and DOC files that you want protected to a particular file set - eg "Restricted". Best way to do this is use the option on the form block which automatically assigns files to a set when the form is submitted.

Step 2, we block direct access to these filetypes via .htaccess. Note that we don't want to be too broad here, because passing files through PHP slows them down big time. Expect big performance problems if you try to protect images using this method.

RewriteRule ^files/(.+\.pdf)$ file_access/$1 [L]
RewriteRule ^files/(.+\.doc)$ file_access/$1 [L]
RewriteRule ^files/(.+\.docx)$ file_access/$1 [L]


Step 3, we create a new single page on the site. This is a controller that we pass all PDF and DOC requests through, it will check permissions before serving the file.

You need to upload file_access.php into the controllers folder...
<?php  defined('C5_EXECUTE') or die("Access Denied.");
class FileAccessController extends Controller {
    var $restricted_sets = array('Restricted');
    function view($arg1=false, $arg2=false, $arg3=false, $arg4=false, $arg5=false, $arg6=false, $arg7=false, $arg8=false, $arg9=false, $arg10=false) {
        $uri = false;
        for ($i=0; $i<=10; $i++) {
            if (!empty(${'arg'.$i})) $uri .= '/'.${'arg'.$i};
        }
        if (!empty($uri)) $uri = 'files'.$uri;
        $basedir = realpath(dirname(__FILE__).'/../');
        if (!file_exists($basedir.'/'.$uri)) {
            echo 'File does not exist';
            exit;
        }
        $db = Loader::db();


And you need to upload a stub into the single_pages folder (file_access.php again)...
<?php  defined('C5_EXECUTE') or die("Access Denied.");
//this is an empty file


Then go to the dashboard and add a single page caled "file_access". Then go to sitemap and set the attributes so it doesn't show on the menu or in the sitemap etc.

Now - this should be working.

When someone requests a file - eg /files/1234/5678/9012/test.pdf Apache will pass that request to the file_access.php controller instead of serving direct.

The controller looks in the database for test.pdf and finds the correct one if there are several. This is slightly hacky as there is no C5 method for looking up a file by URL (AFAIK). It then looks for the sets the file belongs to and sees if it's in the 'Restricted' set. If it is, then you get an error message instead of the file. If it doesn't belong to that set, or if you are an administrator, then it serves the file to the browser.

The net result is that you can protect PDF and DOC files that belong to a particular set, even if someone knows the URL. This will slow down the performance of these types of files as you are introducing PHP and some DB queries, but won't affect the speed of images which is the bigger concern. It will NOT protect any filetype that is not specified in your .htaccess rules, even if you add it to the restricted group.

Hopefully this solution helps some people. Like many, I was not happy with the concept that private files are unprotected in the file manager if someone knows the URL. This strikes a good balance between security, performance, and ease of install I think.

I'd welcome any comments or improvements on this. Note I whipped this up pretty quick so there are definitely improvements that can be made.
c5dragon replied on at Permalink Reply
c5dragon
Guessing this is for C5.6? I'm trying to make this work for 5.7.
(this works for 5.6 if clean urls is enabled)

For 5.7, I changed the 'files' dir to 'application/files' dir but getting 404.
Miriable replied on at Permalink Reply
Okay, I think I got this set up for 5.7+. Single pages have to be namespaced now, and some of the core functions have changed. Here's the new controller:
<?php
namespace Application\Controller\SinglePage;
use PageController;
class FileAccess extends PageController {
    var $restricted_sets = array('Restricted');
    function view($arg1=false, $arg2=false, $arg3=false, $arg4=false, $arg5=false, $arg6=false, $arg7=false, $arg8=false, $arg9=false, $arg10=false) {
        $uri = false;
        for ($i=0; $i<=10; $i++) {
            if (!empty(${'arg'.$i})) $uri .= '/'.${'arg'.$i};
        }
        if (!empty($uri)) $uri = '/application/files'.$uri;
        $basedir = realpath(dirname(__FILE__).'/../../../');
        if (!file_exists($basedir.'/'.$uri)) {
            http_response_code(404);
         echo '<h1 class="error">File Not Found</h1>';

I've reformatted the error codes to look more Apache-like; if you have custom error pages set up, that's not necessary.

Note the new Permissions object in the hasAccess function. If you have advanced permissions turned on, you can set per-file permissions and ensure that the currently logged-in user has explicit access to each file.

The other files in Sadu's setup can stay as they are.