I have a LAMP setup and I just want to be able to protect content on the webpage (images,css,videos,etc) so that only logged in users can access it.
I realize I can do this easily with .htaccess. However I do not want to use the authentication popup, and I want to be able to use sessions and also be able to logout.
I am using php to do the job of authenticating with mysql and create sessions. This works great. But images, css, javascript etc are still accessible.
How do I allow access to the content only if a valid php session exists?
I have come across using mod_rewrite to forward files to a php file (like auth.php?file=...) and do session checking there. This seems inefficient to check the session for every single image in a page that has already been checked. It seems like a hack and I keep thinking there is a cleaner way of doing this.
Is there a mod for apache like mod_session_cookie that could check if a cookie with a session key exists in my session database and if so sets Allow from all for the directory?
Alternatively, is it possible to use mod_auth_mysql but also be able to use sessions and login using a php form and not the authentication popup?
EDIT:
Here is my solution to the problem:
In my apache configuration file (not .htaccess) I added:
RewriteLock /var/www/lib/rewrite.lock
<VirtualHost>
#...
RewriteEngine on
RewriteMap sessionValid prg:/var/www/lib/allow.php
<Directory /var/www/client/*>
RewriteEngine on
RewriteCond %{HTTP_COOKIE} !client-cookie=([^;]+)
RewriteRule .* - [L,R=403]
RewriteCond %{HTTP_COOKIE} client-cookie=([^;]+)
RewriteCond ${sessionValid:%1} !valid
RewriteRule .* - [L,R=403]
</Directory>
</VirtualHost>
And the script allow.php:
#!/usr/bin/php5
<?php
set_time_limit(0);
echo "";
$stdin = fopen("php://stdin","r");
$db = mysql_connect(...);
mysql_select_db(..., $db);
$querypre = "SELECT ID FROM Sessions WHERE ID='";
while (1) {
$line = trim(fgets($stdin));
$query = $querypre.mysql_real_escape_string($line)."'";
$result = mysql_query($query);
if (mysql_num_rows($result) > 0)
echo("valid\n");
else
echo("0\n");
}
mysql_close($db);
?>
This works like a charm. Using this and session_set_save_handler I was able to use php sessions backed by mysql to secure both the php pages and all content within. I hope someone finds this useful.
Some caveats:
The RewriteMap statement needs to be defined inside the virtual host block if you are going to use it inside that virtual host. Placing it outside of the block will not work.
You must have set RewriteEngine on before defining the RewriteMap or it will be ignored.
RewriteLock cannot be in the virtual host block.
As with any shell script, the php file must be executable by the apache user and void of ^M's
RewriteMap statements cannot be placed in .htaccess but the other statements that use the map can.
RewriteCond %{HTTP_COOKIE} !mysessioncookie=([^;]+)
RewriteRule .+\.(jpg|css|js) forbidden.html [R=403]
I actually don't use Apache; I use lighttpd, which includes a plug-in that you can use to restrict access to a folder based on if a correct hash is passed in the URI. Basically, your application logic and the webserver share a secret salt that is then used to generate a hash with the current time, which is also passed with the hash. If the time is within the past 5 minutes, then it grants access. If not, or if the hash is invalid, then it doesn't. I'm googling around to see if there's a similar plugin for Apache at the moment.
Edit: mod_auth_token appears to do the same thing for Apache.
Instead of linking straight to the resources, use some form of controller to serve the images.
For example, linking to 'images/bob.jpg' wouldn't actually point to a resource, but a script, which checks login, and if successful, sends image/jpeg headers with the correct data.
Related
Working on a dev site for a client, we want to deny all access from it, but allow easy whitelisting when out of office for meetings or working from home (dynamic ip).
What we want to happen, is have a form, that writes your IP address to the htaccess file along with a comment above it stating who this is or who authorized it etc.
Without going into a bunch of details, a simple password wont work in our case, having people monitor email accounts for requests, having clients obtain their own IP addresses, things like this just wont fly.
What would be nice, is allowing these added IP addresses in htaccess to expire. So I figure complicated logic like that wont fly in htaccess itself, so it would need to be managed by a 3rd party software, unless anyone has any other ideas?
I recommend using Apache's RewriteMap directive. Please note that to use the RewriteMap directive you have to place the directive in the httpd.conf and NOT the .htaccess file. You can use it in several simple ways.
Plain text file
The plain text version allows you to have a .txt file that holds the ip addresses. I added a line for a comment. This way doesn't allow auto expiration.
httpd.conf
RewriteEngine on
RewriteMap ipmap txt:/path/to/whitelist.txt
RewriteCond ${ipmap:%{REMOTE_ADDR}} !^allow$ [NC]
RewriteRule .* - [F,L]
whitelist.txt
# Chris London added this 2013/06/14
127.0.0.1 allow
123.45.67.89 allow # Some other comment
Custom Program
With the RewriteMap you can actually have it run an external program but this one comes with some caveats. I personally haven't used this method especially with a PHP script. To make it work with a PHP script, I believe, it has to run indefinitely reading the stdin and writing to the stdout.
RewriteEngine on
RewriteLock /path/to/rewrite.lock
Rewritemap ipmap prg:/path/to/executable.php
RewriteCond ${ipmap:%{REMOTE_ADDR}} !^allow$ [NC]
RewriteRule .* - [F,L]
executable.php
#!/usr/bin/php
<?php
$in = fopen('php://stdin', 'r');
$out = fopen('php://stdout', 'r');
while ($ip = fgets($f)) {
// TODO add better logic
if ($ip == '127.0.0.1') {
fwrite(out, 'allow');
} else {
fwrite(out, 'deny');
}
}
fclose($f);
Keep your rewrite map program as simple as possible. If the program hangs, it will cause httpd to wait indefinitely for a response from the map, which will, in turn, cause httpd to stop responding to requests.
Be sure to turn off buffering in your program. Buffered I/O will cause httpd to wait for the output, and so it will hang.
Remember that there is only one copy of the program, started at server startup. All requests will need to go through this one bottleneck. This can cause significant slowdowns if many requests must go through this process, or if the script itself is very slow.
DB Query
I also haven't used this one yet but it looks pretty neat. mod_dbd will need to be configured to point at the right database for this to work. You have a SQL statement that fetchs the ip addresses and you can add a filter for the expiration date.
RewriteEngine on
RewriteMap ipmap "dbd:SELECT ipaddress FROM rewrite WHERE expiration < TIME() and ipaddress = %s"
RewriteCond ${ipmap:%{REMOTE_ADDR}} !^%{REMOTE_ADDR}$ [NC]
RewriteRule .* - [F,L]
There are a couple other types out there but these seem to be the best fit for you. Like I said before I haven't used the Custom Program or the DB Query before so I may have said something wrong. Hopefully another user on here may catch my mistake so these will all work for you.
I'm working on a solution to a problem where users could potentially access images (in this case PDF files) stored in a folder off the server root. Normally, my application validates users through PHP scripts and sessions. What isn't happening right now is preventing non-logged in users from potentially accessing the PDFs.
The solution I'm looking for would (I think) need to be tied in with Apache. I saw an interesting solution using RewriteMap & RewriteRule, however the example involved putting this in an .htaccess file in the PDF directory. Can't do that with Apache (error: RewriteMap not allowed here). I believe the rewrite directives need to go in my httpd.conf, which I have access to.
So the example I found (that resulted in 'rewritemap not allowed here') is here:
RewriteEngine On
RewriteMap auth prg:auth.php
RewriteRule (.*) ${auth:$1}
auth.php just checks PHP session and redirects to a login script if needed.
I'm reading that I have to place this in my httpd.conf. How would I specify that the RewriteMap should only occur on a specific directory (including subdirectories)?
1st, be sure that you have to put that directly in httpd.conf. On Debian system, for instance, you have 1 file by virtualhost (a virtualhost usually is a website)
So, you have to put your rewriteMap in a "directory" like this:
<Directory /full/path/to/your/pdfs>
RewriteEngine on
...
</Directory>
I have a protected folder. I want people who are logged in (via PHP / WordPress) to have access to the folder and the files therein.
Those who are not logged in should be redirected via .htaccess.
Can the .htaccess rewrite condition be based off an environment variable or a server variable which I added or edited from PHP?
UPDATE:
See my answer below.
.htaccess (hypertext access) file is a directory-level configuration file supported by several web servers. I can't think of any simple way of how you could access runtime variables set in PHP with .htaccess as .htaccess allows no "execution" of commands, it is just a bunch of config directives.
You could maybe do some sort of VERY VERY strange combination of .htaccess and CGI scripts and maybe more to access a webservice # PHP level, but that would be far beyond my programming skills and I suppose beyond those of most PHP developers too...
At least this is what I can tell you, I would be interestd too if someone knows a hack for this...
The easiest way of how to do such redirects would in my opinion be header("Location: xxx.html"); directly in PHP.
You can't edit the .htaccess file on the fly using PHP to set these variables. I mean, you can, but the .htaccess file is used by the entire server, not per-user. Unless you wanted to do some ridiculous write-username-environment-variables to .htaccess and hope it works somehow, you're much better off just doing this via php. If they're not logged in, you can redirect them away with PHP so they won't be able to see the protected folders either.
If you want to keep them out of an entire folder but you don't want to do something like require security-check.php on every file, you could look into using auto_prepend_file. You could also use your .htaccess to route all file access through one specific php file that does this. You would need to do this if you were keeping people out of non-php files.
After much research, I solved it.
My folder system is setup like this:
/file-share/users-folder-name
My .htaccess file under /file-share is as follows:
# .htaccess /file-share
RewriteEngine On
RewriteBase /
# no cookie set
RewriteCond %{HTTP_COOKIE} !^.*client.*$ [NC]
RewriteRule ^(.*)$ /file-share-redirect.php?q=$1 [NC,L]
# cookie set
RewriteCond %{QUERY_STRING} !verified$ [NC]
RewriteCond %{HTTP_COOKIE} client=([^;]+) [NC]
RewriteRule ^(.*)$ /file-share/%1/$1?verified [NC,L,QSA]
# custom 404
ErrorDocument 404 /file-share-redirect.php?e=404&q=$1
#end
If the cookie is set and the file exists in the client's folder then the client is redirected seamlessly to the requested file. The final file request is also given a url parameter to avoid a loop in redirection.
If a user is logged but the cookie is not set I have my file-share-redirect.php file create the cookie then redirect to the requested file. The cookie created in the code below is set to expire in an hour.
<?php setcookie('client', $users_folder_name, time()+3600); ?>
UPDATE
You can keep the cookie secure by using an encrypted cookie name and value. The cookie will only be created on systems where users log in.
PHP's setcookie() will even let you create a cookie that is inaccessible from JavaScript. I double checked this.
The subfolder names will be quite complex, completely unguessable. No one will ever see the subfolder names except those with ftp access. Even those logged in will only see /_/filename.ext, without the subfolder.
I have a LAMP setup and I just want to be able to protect content on the webpage (images,css,videos,etc) so that only logged in users can access it.
I realize I can do this easily with .htaccess. However I do not want to use the authentication popup, and I want to be able to use sessions and also be able to logout.
I am using php to do the job of authenticating with mysql and create sessions. This works great. But images, css, javascript etc are still accessible.
How do I allow access to the content only if a valid php session exists?
I have come across using mod_rewrite to forward files to a php file (like auth.php?file=...) and do session checking there. This seems inefficient to check the session for every single image in a page that has already been checked. It seems like a hack and I keep thinking there is a cleaner way of doing this.
Is there a mod for apache like mod_session_cookie that could check if a cookie with a session key exists in my session database and if so sets Allow from all for the directory?
Alternatively, is it possible to use mod_auth_mysql but also be able to use sessions and login using a php form and not the authentication popup?
EDIT:
Here is my solution to the problem:
In my apache configuration file (not .htaccess) I added:
RewriteLock /var/www/lib/rewrite.lock
<VirtualHost>
#...
RewriteEngine on
RewriteMap sessionValid prg:/var/www/lib/allow.php
<Directory /var/www/client/*>
RewriteEngine on
RewriteCond %{HTTP_COOKIE} !client-cookie=([^;]+)
RewriteRule .* - [L,R=403]
RewriteCond %{HTTP_COOKIE} client-cookie=([^;]+)
RewriteCond ${sessionValid:%1} !valid
RewriteRule .* - [L,R=403]
</Directory>
</VirtualHost>
And the script allow.php:
#!/usr/bin/php5
<?php
set_time_limit(0);
echo "";
$stdin = fopen("php://stdin","r");
$db = mysql_connect(...);
mysql_select_db(..., $db);
$querypre = "SELECT ID FROM Sessions WHERE ID='";
while (1) {
$line = trim(fgets($stdin));
$query = $querypre.mysql_real_escape_string($line)."'";
$result = mysql_query($query);
if (mysql_num_rows($result) > 0)
echo("valid\n");
else
echo("0\n");
}
mysql_close($db);
?>
This works like a charm. Using this and session_set_save_handler I was able to use php sessions backed by mysql to secure both the php pages and all content within. I hope someone finds this useful.
Some caveats:
The RewriteMap statement needs to be defined inside the virtual host block if you are going to use it inside that virtual host. Placing it outside of the block will not work.
You must have set RewriteEngine on before defining the RewriteMap or it will be ignored.
RewriteLock cannot be in the virtual host block.
As with any shell script, the php file must be executable by the apache user and void of ^M's
RewriteMap statements cannot be placed in .htaccess but the other statements that use the map can.
RewriteCond %{HTTP_COOKIE} !mysessioncookie=([^;]+)
RewriteRule .+\.(jpg|css|js) forbidden.html [R=403]
I actually don't use Apache; I use lighttpd, which includes a plug-in that you can use to restrict access to a folder based on if a correct hash is passed in the URI. Basically, your application logic and the webserver share a secret salt that is then used to generate a hash with the current time, which is also passed with the hash. If the time is within the past 5 minutes, then it grants access. If not, or if the hash is invalid, then it doesn't. I'm googling around to see if there's a similar plugin for Apache at the moment.
Edit: mod_auth_token appears to do the same thing for Apache.
Instead of linking straight to the resources, use some form of controller to serve the images.
For example, linking to 'images/bob.jpg' wouldn't actually point to a resource, but a script, which checks login, and if successful, sends image/jpeg headers with the correct data.
My PHP app uses 404 Documents to generate HTML files so that multiple queries to the same HTML file only cause the generation to run once.
I'd like to intercept requests to the HTML files so that the user needs to have an established PHP Session in order to pull up the files.
In the best case, SESSION ID would be used in the URL and force it could be used as a further authentication. For example, logging in would issue you a SessionID and make only certain HTML files accessible to you.
I'm aware that by changing my cookies I could spoof a request, but that's fine.
How would I go about doing this?
Something like this could work (I haven't tested it):
RewriteCond %{HTTP_COOKIE} PHPSESSID=([a-zA-Z0-9]+)
RewriteCond %{REQUEST_FILENAME} %{REQUEST_FILENAME}-%1.html
RewriteRule ^ %{REQUEST_FILENAME}-%1.html
It assumes that you append "-$session_id.html" to filenames ($session_id is PHP's session ID).
It should be safe, and the benefit is that files are served by the web server directly without invoking PHP at all.
SetEnvIf HTTP_COOKIE "PHPSESSID" let_me_in
<Directory /www/static/htmls>
Order Deny,Allow
Deny from all
Allow from env=let_me_in
</Directory>
Of course user can manually create such cookie in his browser (there are extensions which do that for Firefox, and you can always edit your browser's cookie store).
You could use the Apache module mod_rewrite to redirect requests of .html URLs to a PHP script:
RewriteEngine on
RewriteRule \.html$ script.php [L]
The requested URI path and query is then available in the $_SERVER['REQUEST_URI'] variable.
Put you cached files out of your web root, but still in a place where PHP can access them.