Firewall/WAF with PHP: Whitelist and blacklist with IP addresses for PHP scripts
Today I would like to publish a short article with some examples how a firewall/WAF can be implemented with PHP.
I have created 4 examples including a whitelist, a blacklist, a blacklist with a text file as IP blocklist and an example for the implementation of a mini-WAF.
All code examples are designed to be inserted in the upper part of already existing scripts.
PHP Whitelist
The allowed IP addresses are in the array $waf – which of course can be extended.
If the IP address is not in the list, access is denied and the message “Blocked 1” is displayed, otherwise the script continues to run as planned. In this example “Hello World 1” will be output.
// PHPWAF START - WHITELIST EXAMPLE
$waf = array(
'127.0.0.1',
'1.1.1.1',);
$rip = isset($_SERVER['REMOTE_ADDR']) ? trim($_SERVER['REMOTE_ADDR']) : '';
echo $rip; //echo the IP for testing
if ((($key = array_search($rip, $waf)) == false)) {
echo 'Blocked 1';
exit();
}
echo 'Hello World 1'
// PHPWAF END - WHITELIST EXAMPLE
PHP Blacklist
In the next example, we reverse the process and the IPs in the $waf array become a blacklist. However, this can be error-prone – if Cloudflare is used, for example – which is why we ask for the will “CF_CONNECTING_IP” and “X_FORWARDED_FOR” in addition to the normally determined IP address and match them with our blacklist. This increases the hit rate significantly.
// PHPWAF START - BLACKLIST EXAMPLE
$waf = array(
'0.0.0.0',
'1.1.1.1',
);
$rip = ''; $cip = ''; $xip = '';
$rip = isset($_SERVER['REMOTE_ADDR']) ? trim($_SERVER['REMOTE_ADDR']) : '';
$cip = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? trim($_SERVER['HTTP_CF_CONNECTING_IP']) : ''; //rec: dont use for whitelist
$xip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? trim($_SERVER['HTTP_X_FORWARDED_FOR']) : ''; //rec: dont use for whitelist
//echo $rip.$cip.$xip; //echo the IPs for testing
if ((($key = array_search($rip, $waf)) == true) || (($key = array_search($cip, $waf)) == true) || (($key = array_search($xip, $waf)) == true)) {
echo 'Blocked 2';
exit();
}
echo 'Hello World 2';
// PHPWAF END - BLACKLIST EXAMPLE
PHP blacklist with a blocklist
This example follows the blacklist example above. However, no array is used for the blacklist, but a text file with the name “blacklist.txt”. The IP addresses can be entered there line by line.
// PHPWAF START - BLACKLIST EXAMPLE WITH FILE
$waf = file('blacklist.txt', FILE_IGNORE_NEW_LINES);
$rip = ''; $cip = ''; $xip = '';
$rip = isset($_SERVER['REMOTE_ADDR']) ? trim($_SERVER['REMOTE_ADDR']) : '';
$cip = isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? trim($_SERVER['HTTP_CF_CONNECTING_IP']) : ''; //rec: dont use for whitelist
$xip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? trim($_SERVER['HTTP_X_FORWARDED_FOR']) : ''; //rec: dont use for whitelist
//echo $rip.$cip.$xip; //echo the IPs for testing
if ((($key = array_search($rip, $waf)) == true) || (($key = array_search($cip, $waf)) == true) || (($key = array_search($xip, $waf)) == true)) {
echo 'Blocked 3';
exit();
}
echo 'Hello World 3';
// PHPWAF END - BLACKLIST EXAMPLE WITH FILE
mini-WAF with PHP
This is just an example of an extremely simplified version of a WAF. In this example all values – which are passed to the PHP script via POST or GET – are searched for certain strings to prevent e.g. cross-site scripting and simple SQL injections. Of course, this is by no means sufficient or complete.
//PHPWAF - INPUT CHECKS START
$postdata = file_get_contents("php://input");
if($postdata) {
if ((strstr($postdata, 'exec '))||(strstr($postdata, 'SELECT *'))||(strstr($postdata, 'select *'))||(strstr($postdata, 'UNION SELECT'))||(strstr($postdata, 'union select'))||(strstr($postdata, 'EXEC '))||(strstr($postdata, '<script>'))||(strstr($postdata, '<SCRIPT>'))||(strstr($postdata, '<?php'))||(strstr($postdata, '<?'))||(strstr($postdata, '?>'))||(strstr($postdata, 'UNION DELETE'))||(strstr($postdata, 'union delete'))||(strstr($postdata, 'UNION UPDATE'))||(strstr($postdata, 'union update'))||(strstr($postdata, 'UNION TRUNCATE'))||(strstr($postdata, 'union truncate')))) { //str_contains for php8
echo "blocked 4";
exit;
}
}
$postdata = $_GET;
$postdata = implode(" ", $postdata);
if($postdata) {
if ((strstr($postdata, 'exec '))||(strstr($postdata, 'SELECT *'))||(strstr($postdata, 'select *'))||(strstr($postdata, 'UNION SELECT'))||(strstr($postdata, 'union select'))||(strstr($postdata, 'EXEC '))||(strstr($postdata, '<script>'))||(strstr($postdata, '<SCRIPT>'))||(strstr($postdata, '<?php'))||(strstr($postdata, '<?'))||(strstr($postdata, '?>'))||(strstr($postdata, 'UNION DELETE'))||(strstr($postdata, 'union delete'))||(strstr($postdata, 'UNION UPDATE'))||(strstr($postdata, 'union update'))||(strstr($postdata, 'UNION TRUNCATE'))||(strstr($postdata, 'union truncate')))) { //str_contains for php8
echo "blocked 4";
exit;
}
}
echo 'Hello World 4';
echo file_get_contents("php://input");
//PHPWAF - INPUT CHECKS END