Firewall/WAF mit PHP: Whitelist und Blacklist mit IP-Adressen für PHP-Scripte
Heute möchte ich hier einen kurzen Artikel mit einigen Beispielen wie eine Firewall/WAF mit PHP umgesetzt werden kann veröffentlichen.
Ich habe hierzu 4 Beispiele erstellt darunter eine Whitelist, eine Blacklist, eine Blackliste mit einer Textdatei als IP-Blockliste und ein Beispiel zur Umsetzung einer mini-WAF.
Alle Codebeispiele sind darauf ausgelegt im oberen Bereich bereits vorhandener Scripte eingefügt zu werden.
PHP Whitelist
Die erlaubten IP-Adressen befinden sich im Array $waf – welches natürlich auch erweitert werden kann.
Ist die IP-Adresse nicht in der Liste enthalten wird der Zugriff verweigert und die Meldung „Blocked 1“ ausgegeben, anderenfalls läuft das Script wie geplant weiter. In diesem Beispiel wird dann „Hello World 1“ ausgegeben.
// 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
Im nächsten Beispiel drehen wir das Verfahren um und die IPs im Array $waf werden zu einer Blacklist. Das kann jedoch fehleranfällig sein – wenn z. B. Cloudflare genutzt wird – weshalb wir zusätzlich zur normal ermittelten IP-Adresse nach den Werden „CF_CONNECTING_IP“ und „X_FORWARDED_FOR“ fragen und mit unserer Blackliste abgleichen. Das erhöht die Trefferquote maßgeblich.
// 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 mit einer Blockliste
Dieses Beispiel folgt dem obigen Beispiel einer Blacklist. Es wird jedoch kein Array für die Blackliste genutzt, sondern eine Textdatei mit dem Namen „blacklist.txt“. Die IP-Adressen können dort Zeile für Zeile eingetragen werden.
// 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 mit PHP
Das ist lediglich ein Beispiel einer extrem vereinfachten Version einer WAF. In diesem Beispiel werden alle Werte – welche per POST oder GET an das PHP-Script übergeben werden – nach bestimmten Strings durchsucht um z. B. Cross-Site-Scripting und einfache SQL-Injections zu unterbinden. Natürlich ist das keinesfalls ausreichend oder vollständig.
//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