Reputation: 6814
Lets say you have lots of html, css, js, img and etc files within a directory on your server. Normally, any user in internet-land could access those files by simply typing in the full URL like so: http://example.com/static-files/sub/index.html
Now, what if you only want authorized users to be able to load those files? For this example, lets say your users log in first from a URL like this: http://example.com/login.php
How would you allow the logged in user to view the index.html file (or any of the files under "static-files"), but restrict the file to everyone else?
I have come up with two possible solutions thus far:
Solution 1
Create the following .htaccess file under "static-files":
Options +FollowSymLinks
RewriteEngine on
RewriteRule ^(.*)$ ../authorize.php?file=$1 [NC]
And then in authorize.php...
if (isLoggedInUser()) readfile('static-files/'.$_REQUEST['file']);
else echo 'denied';
This authorize.php file is grossly over simplified, but you get the idea.
Solution 2
Create the following .htaccess file under "static-files":
Order Deny,Allow
Deny from all
Allow from 000.000.000.000
And then my login page could append that .htaccess file with an IP for each user that logs in. Obviously this would also need to have some kind of cleanup routine to purge out old or no longer used IPs.
Which of these solutions sounds better, and why? Alternatively, can you think of a completely different solution that would be better than either of these?
Upvotes: 40
Views: 37938
Reputation: 17215
I might have a suggestion based on iframe
and HTTP_REFERER
but its not bullet proof and it will depend on what exactly you want to protect with this access.
But in the case of preventing a full static page to be displayed without authentication you can do as follow:
1 - use a PHP page to authenticate the user
2 - redirect to another PHP page containing a key in the URL and an iframe linking to your static content in the body:
<iframe src="static/content.html" />
3 - Then in your htaccess you could check the key inside the HTTP_REFERER like that:
RewriteEngine On
RewriteCond %{HTTP_REFERER} !AUTH_KEY
RewriteCond %{REQUEST_URI} ^/path/to/protected/page$
RewriteRule . - [F]
4 - Finally if you want to make it more dynamic and not use the same KEY every time you could use rewrite map
as suggested by Ignacio Vazquez-Abrams' answer or create a file using the user IP as filename and check if file exists using REMOTE_ADDR
then remove the file after a while.
But keep in mind that iframe + HTTP_REFERER behavior might vary from one browser session to another as well as REMOTE_ADDR so its limited...
Upvotes: 0
Reputation: 1687
There's a module for Apache (and other HTTP servers) which lets you tell the HTTP server to serve the file you specify in a header in your php code: So your php script should look like:
// 1) Check access rights code
// 2) If OK, tell Apache to serve the file
header("X-Sendfile: $filename");
2 possible problems:
Here's a good answer in another thread: https://stackoverflow.com/a/3731639/2088061
Upvotes: 10
Reputation: 179
Assume you want to protect all your static files and you have to server them from inside your webroot, you can protect all HTTP-Methods except HEAD. If you are authorized, you make a head request pass through the headers and send the filecontent as body. Sure this is expensive, but you are protected and you have the same behaviour.
Upvotes: 0
Reputation: 1
If you are using apache, you can configure, as below, in either .htaccess or httpd.conf file. Below is an example to prevent access to *.inc file. It greatly works for me.
<Files ~ "\.inc$">
Order allow,deny
Deny from all
</Files>
Please refer for more detail at: http://www.ducea.com/2006/07/21/apache-tips-tricks-deny-access-to-certain-file-types/.
Upvotes: -1
Reputation: 2951
I wrote a dynamic web application and deployed it on Webshere Application Server, and here is the way how I secured my static files :
I first added
<login-config id="LoginConfig_1">
<auth-method>FORM</auth-method>
<realm-name>Form-Based Authentication</realm-name>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/login_error.html</form-error-page>
</form-login-config>
</login-config>
in web.xml which will tell your webserver to use form based authentication(code to use login is given below).
code to login page :
<form id="form1" name="form1" method="post" action="j_security_check" style="padding: 0px 0px 0px 12px;">
Username:
<label>
<input name="j_username" type="text" class="font2" />
</label>
<br />
<br />
Password:
<span class="font2" >
<label>
<input name="j_password" type="password" class="font2" />
</label>
</span>
<br />
<br />
<label>
<input type="submit" class="isc-login-button" name="Login" value="Login" />
</label>
</form></td>
To make form based login happen you have to configure your webserver to use a particular user registory which can be LDAP or database.
You can declare your secure resources and whenever a user tries to access those resources container automatically checks that whether user is authenticated or not. Even you can attach roles also with the secure resources. To do this I have added following code in my web.xml
<security-constraint>
<display-name>Authenticated</display-name>
<web-resource-collection>
<web-resource-name>/*</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>HEAD</http-method>
<http-method>TRACE</http-method>
<http-method>POST</http-method>
<http-method>DELETE</http-method>
<http-method>OPTIONS</http-method>
</web-resource-collection>
<auth-constraint>
<description>Auth Roles</description>
<role-name>role1</role-name>
<role-name>role2</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>role1</role-name>
</security-role>
<security-role>
<role-name>role2</role-name>
</security-role>
So this code will not let user see any static file( since /*) until he is logged in under role role1 and role2. So by this way you can protect your resources.
Upvotes: 0
Reputation: 449613
I have been thinking a lot about the same issue. I am equally unhappy with the PHP engine running for every small resource that is served out. I asked a question in the same vein a few months ago here, though with a different focus.
But I just had an awfully interesting idea that might work.
Maintain a directory called /sessions
somewhere on your web server.
Whenever a user logs in, create an empty text file with the session ID in /sessions
. E.g. 123456
In your PHP app, serve out images like this: /sessions/123456/images/test.jpg
In your htaccess file, have two redirect commands.
One that translates /sessions/123456/images/test.jpg
into /sessions/123456?filename=images/test.jpg
A second one that catches any calls to //sessions/(.*)
and checks whether the specified file exists using the -f
flag. If /sessions/123456
doesn't exist, it means the user has logged out or their session has expired. In that case, Apache sends a 403 or redirects to an error page - the resource is no longer available.
That way, we have quasi-session authentication in mod_rewrite doing just one "file exists" check!
I don't have enough routine to build the mod_rewrite statements on the fly, but they should be easy enough to write. (I'm looking hopefully in your direction @Gumbo :)
Notes and caveats:
Expired session files would have to be deleted quickly using a cron job, unless it's possible to check for a file's mtime in .htaccess (which may well be possible).
The image / resource is available to any client as long as the session exists, so no 100% protection. You could maybe work around this by adding the client IP into the equation (= the file name you create) and do an additional check for %{REMOTE_ADDR}. This is advanced .htaccess mastery but I'm quite sure it's doable.
Resource URLs are not static, and have to be retrieved every time on log-in, so no caching.
I'm very interested in feedback on this, any shortfalls or impossibilities I may have overlooked, and any successful implementations (I don't have the time right now to set up a test myself).
Upvotes: 4
Reputation: 799082
Create a rewrite map that verifies the user's credentials and either redirects them to the appropriate resource or to an "access denied" page.
Upvotes: 4
Reputation: 48367
Maintaining the contents of the htaccess files looks to be a nightmare. Also, your stated objective is to prevent non-authenticated users not non-authenticated client ip addresses from accessing this content - so the approach is not fit for purpose:
Multiple users can appear to come from the same IP address
A single users session may appear to come from multiple addresses.
I worry that my first solution could get pretty expensive on the server as the number of users and files they are accessing increases
If you want to prevent your content from leaching and don't want to use HTTP authenitcation then wrapping all file access in an additional layer of logic is the only sensible choice. Also, you don't know that using PHP for this is a problem - have you tested it? I think you'd be surprised just how much throughput it could deliver, particularly if you use an opcode cache.
I'm guessing your 'simplification' of the wrapper addresses issues like mime type and caching.
C.
Upvotes: 2
Reputation: 16817
I would consider using a PHP loader to handle authentication and then return the files you need. For example instead of doing <img src='picture.jpg' />
Do something like <img src='load_image.php?image=picture.jpg' />
.
Your image loader can verify sessions, check credentials, etc. and then decide whether or not to return the requested file to the browser. This will allow you to store all of your secure files outside of the web accessible root so nobody is going to just WGET them or browse there 'accidentally'.
Just remember to return the right headers in PHP and do something like readfile() in php and that will return the file contents to the browser.
I have used this very setup on several large scale secure website and it works like a charm.
Edit: The system I am currently building uses this method to load Javascript, Images, and Video but CSS we aren't very worried with securing.
Upvotes: 36