Walt Walt - 6 months ago 42
PHP Question

Integrating PHP IP filter in .htaccess

I have an htaccess file with a lot of 'deny from ' entries. Most of these with common lower octets.

To try and improve htaccess processing time, for IPs that are not banned, I am trying to implement a php filter, which is called from a rewrite rule.

My htaccess rewrite rule:

RewriteCond %{REMOTE_ADDR} ^37\.115\. [OR]
RewriteCond %{REMOTE_ADDR} ^46\.118\.
RewriteRule (.*) check_banned.php?ip=%{REMOTE_ADDR}

... More rewrite rules


Note, there is no '[L]' at the end of the rewrite rule. If the IP is not banned, I would like htaccess to continue processing rules.
The current php file:

$ip = strip_tags(trim($_GET['ip']));

// Read file with list of banned ips
$file = ban_list.txt;
$myfile = fopen($file, "r") or die("Unable to open file!");
$ban_list = fread($myfile, filesize($file));
fclose($myfile);

// Check if ip is in ban list
$pos = strpos($ban_list, $ip);

if($pos === false) $ip_found = false;
else $ip_found = true;

if($ip_found){
header('HTTP/1.0 403 Forbidden');
}
else {
// What to do if ip is not banned?
}


This is working fine if the ip is found in the ban list. However, I cannot find a way to handle the case when the ip is not banned. I just get a blank page, with header '200 ok'.

Also, I was thinking of passing (.*) to the file check_banned.php, convert the php code to a function, which would return the string in case the ip is not banned. Have to admit I have not tried this yet.

RewriteRule (.*) check_banned.php?ip=%{REMOTE_ADDR}&str=$1


In php file:

$string = strip_tags(trim($_GET['str']));

if($ip_found){
header('HTTP/1.0 403 Forbidden');
}
else {
return $string;
}





Update:
Thanks to Mike Nickaloff, here are some tweaks. However, it's just an exploring exercise, since (accepted) the whole approach is a bad idea. Unfortunately I have a shared hosting account, with no access to server settings.

I set the file permissions for the file check_banned.php to 600, and the filtering still appears to work.

Changes to check_banned.php:

<?php
ob_start(); // suppress any output
$ip = strip_tags(trim($_SERVER["REMOTE_ADDR"]));

// Read file with list of banned ips
$file = ban_list.txt;
$myfile = fopen($file, "r") or die("Unable to open file!");
$ban_list = fread($myfile, filesize($file));
fclose($myfile);

// Check if ip is in ban list
$pos = strpos($ban_list, $ip);

if($pos === false) $ip_found = false;
else $ip_found = true;

if($ip_found){
ob_end_clean(); // Allow output
header('HTTP/1.0 403 Forbidden');
}
else{
// ip Was not found in ban list
$file = "";

$uri = "";
if(isset($_SERVER["REQUEST_URI"])){
$uri = strip_tags(trim($_SERVER["REQUEST_URI"]));
}

if($uri == "" || $uri == "/")
$file = $_SERVER['DOCUMENT_ROOT']."/".index.php;
else $file = $_SERVER['DOCUMENT_ROOT'].$uri;

$myfile = fopen($file, "r") or die("Unable to open file!");
$file = fread($myfile, filesize($file));
fclose($myfile);

ob_end_clean(); // Allow output
echo $file;
}
?>


Unfortunately, $_SERVER["REQUEST_URI"] may not be the actual location of the file relative to document root. My htaccess file rewrites some urls to make them shorter.

I think this may be as far as this experiment can go. It's a bit disappointing that there doesn't seem to be a way for an htaccess file to ask a php file what it should do next. Something like this would be nice:

RewriteCond %{REMOTE_ADDR} ^37\.115\. [OR]
RewriteCond %{REMOTE_ADDR} ^46\.118\.
RewriteRule (.*) (execute(){check_banned.php}? "- [F,L]":$1)

Answer

Here's one way you could handle this:

have PHP load whatever page is requested by loading the proper URL

This will only work when requesting PHP scripts from the server, so the htaccess rules have to be slightly changed to match only .php files. Match against .* is a bad idea anyways, but this method isn't exactly the safest way to do something like this. You may want to invest in free firewall like UFW, which allows you to block IP addresses from the entire server, rather than on a per-request basis.

Using a firewall is a much more efficient and secure way to block an IP address.

The method where PHP acts as the firewall is vulnerable to DDoS attacks and Recursion attacks. For example -- what happens if someone requests the page check_ip.php? it has to redirect to somewhere -- so it would redirect to check_ip.php, which would redirect to check_ip.php, and it would redirect to check_ip.php -- so to make a long story short,

DO NOT USE THIS METHOD -- IT IS FOR DEMONSTRATIVE PURPOSES AND IS NOT A REPLACEMENT FOR A FIREWALL.

In theory, if we did use this unsafe method, here's how we would do it:

in .htaccess change it to

 RewriteRule ^([\s\S]+)(.php|.phps)$ /check_banned.php [L]

... More rewrite rules

Then in check_banned.php you could do something like this:

<?php
$ip = strip_tags(trim($_SERVER["REMOTE_ADDR"]));

// Read file with list of banned ips
$file = ban_list.txt;
$myfile = fopen($file, "r") or die("Unable to open file!");
$ban_list = fread($myfile, filesize($file));
fclose($myfile);

// Check if ip is in ban list
$pos = strpos($ban_list, $ip);

if($pos === false) $ip_found = false;
else $ip_found = true;

if($ip_found){
    header('HTTP/1.0 403 Forbidden');
}
else {
    // What to do if ip is not banned?
    $sPage = $_SERVER["PATH_TRANSLATED"];
    if (file_exists($sPage)) {
            include_once($sPage);
    }


}

 ?>

WARNING -- REMEMBER -- DO NOT USE THIS METHOD AS IT IS VULNERABLE TO ATTACKS --