views:

188

answers:

7

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.

+1  A: 

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).

Kevin Peno
I forgot to mention in my question that blocking access to the assets on htaccess level has the additional problem that the page should still be visible to the user working in the CMS. Still, good to know that IIS has Apache like per-directory commands, at least some of them. +1
Pekka
Bah, that's a duh. I knew that was your angle. Sorry, I'm having a long morning.
Kevin Peno
Edited to add hybrid method
Kevin Peno
The hybrid method is a bit of work very good, as it indeed combines the best of both worlds. I will probably go with that.
Pekka
"is a bit of work *but* very good" that is. :)
Pekka
+2  A: 

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.

cgr
The assets are not sensitive as such, only when they belong to an un-published. Symlinks are a very good idea I have not thought of yet, but sadly, I can't rely on those in a shared environment.
Pekka
-- was meant to be "un-published page" above. And +1
Pekka
A: 

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.

metrobalderas
+1  A: 

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.

Scott Saunders
Good idea, but does not provide 100% security. A link to a hidden resource could find its way through, say, the admin user's browser cache. The files need to be out of reach altogether.
Pekka
+1  A: 

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.

Pickle
+1 for the idea, but it would probably have to be tied to an .htaccess file as well to prevent the ugly links caused by the guid.
Kevin Peno
+2  A: 

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.

ntd
Very, *very* interesting idea, it's just not 100% secure as referers can be forged. Hmmmm....
Pekka
A: 

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.

mattbasta
Yes, as I wrote, this is the way I am currently doing it. I am looking to avoid the readfile() bottleneck though, because it applies to every tiny resource that is being called.
Pekka
Then perhaps a hybrid approach is in order: a "protected" assets folder and a "proxied" folder. This is effectively the only way to provide security WITHOUT htaccess (you can have secure, fast, or portable. pick two).
mattbasta