Modul: DNSBL Check

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Modul: DNSBL Check

Beitrag von delinquent » Do 4. Okt 2007, 12:24

Hallo alle zusammen,

passend zur Diskussion über DNS Blacklist Checks habe ich ein kleines Modul geschrieben, was beim Betreten einer Webseite die IP-Adresse des Besuchers mit einer DNS-Blackliste abgleicht und gegebenenfalls den Zugang zur Seite verweigert.

Hier die Modul-Ausgabe:

Code: Alles auswählen

<?php
if (!($edit || $contenido)) {
	cInclude("classes", "contenido/class.client.php");
	cInclude("classes", "class.user.php");
	
	$cApiClient   = new cApiClient($client);
	$clientAdmins = User::getClientAdmins($client);
	
	$cfg["tab"]["spamcache"] = $cfg['sql']['sqlprefix'] . "_spamcache";
	$sql = "CREATE TABLE IF NOT EXISTS `" . $cfg["tab"]["spamcache"] . "` (`ip` VARCHAR(15) NOT NULL, `dnsbl` TINYTEXT NOT NULL DEFAULT '', `lastaccessed` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`ip`))";
	$db->query($sql);
	
	$sql = "DELETE FROM `" . $cfg["tab"]["spamcache"] . "` WHERE ROUND((NOW() - `lastaccessed`) / 60) >= 5";
	$db->query($sql);
	
	$sql = "SELECT * FROM `" . $cfg["tab"]["spamcache"] . "` WHERE `ip`='$_SERVER[REMOTE_ADDR]'";
	$db->query($sql);
	if ($db->next_record()) {
		$dnsbl = $db->f('dnsbl');
		$sql = "UPDATE `" . $cfg["tab"]["spamcache"] . "` SET `lastaccessed`=NOW() WHERE `ip`='$_SERVER[REMOTE_ADDR]'";
		$db->query($sql);
		
		if ($dnsbl)
			dieSpammer($dnsbl);
	} else {
		$dnsbls = explode(",", $cApiClient->getProperty("dnsbls", "servers"));
		if (is_array($dnsbls)) foreach ($dnsbls as $dnsbl) {
			$prefix = $cApiClient->getProperty("dnsbls", $dnsbl);
			if ($prefix) $prefix = ".$prefix";
			
			if ($dnsbl && isSpammer("$_SERVER[REMOTE_ADDR]$prefix", $dnsbl)) {
				$sql = "INSERT INTO `" . $cfg["tab"]["spamcache"] . "`(`ip`, `dnsbl`) VALUES('$_SERVER[REMOTE_ADDR]', '$dnsbl')";
				$db->query($sql);
				dieSpammer($dnsbl);
			}
		}
		
		$sql = "INSERT INTO `" . $cfg["tab"]["spamcache"] . "`(`ip`) VALUES('$_SERVER[REMOTE_ADDR]')";
		$db->query($sql);
	}
}
	
function isSpammer($ip, $dnsbl) {
	if (!function_exists("gethostbyname"))
		return false;
		
	if (!preg_match('/(?:(?:[1-9]|[1-9][0-9]|1[0-9]{2,2}|2[0-4][0-9]|25[0-5])\.){4,4}/', "$ip."))
		return false;

	$hostname = join(".", array_reverse(explode(".", $ip))) . ".$dnsbl";
	
	return gethostbyname($hostname) != $hostname;
}

function dieSpammer($dnsbl) {
	global $clientAdmins;

	header("HTTP/1.0 403 Forbidden");
	header("Content-type: text/plain");

	if (is_array($clientAdmins))
		$admin = array_shift($clientAdmins);
		
	die(sprintf("Sorry!\n\n%s says you are a spammer and we don't want spammers to use our services!\n" . (@$admin["email"] ? sprintf("If you think this is incorrect, please drop us a line via email to: %s.\n", str_replace("@", "(AT)", $admin["email"])) : '') . "\nThanks a lot.", $dnsbl));
}
?>
Welche DNS-Server befragt werden sollen, kann in den Mandanteneinstellungen vorgenommen werden:

Typ: dnsbls
Name: servers
Wert: <Komma-getrennte Liste von Servern>

Project Honey Pot beispielsweise braucht noch einen Zugangsschlüssel, solche Schlüssel können als

Typ: dnsbls
Name: <Hostname des Servers wie in der Liste>
Wert: Zugangsschlüssel

hinterlegt werden. Das Abfrageergebnis wird in der Datenbank zwischengespeichert und nach 5 minütiger Inaktivität des Besuchers
wieder entfernt.

Nachtrag: Das Modul muss als erstes im Layout der Seite geladen werden, also noch vor der Dokumenttypdeklaration, da die Ausgabe der Seite im Falle eines Spammers abgebrochen - und ein HTTP 403-Header gesendet wird.

Dodger77
Beiträge: 3626
Registriert: Di 12. Okt 2004, 20:00
Wohnort: Voerde (Niederrhein)
Kontaktdaten:

Beitrag von Dodger77 » Do 11. Okt 2007, 07:40

Cooles Modul, habe ich gleich ausprobiert.
Der Einsatz mit der http:BL (Project Honey Pot) funktioniert allerdings nicht. Dabei wird jede IP-Adresse als Spammer ausgemacht. Zum einen stimmt die Reihenfolge nicht (die Abfrage im Modul ist IP.Schlüssel.DNSBL-Server, müsste aber Schlüssel.IP.DNSBL-Server sein), zum anderen gibt die http:BL anscheinend immer ein detailiertes Ergebnis zurück, so dass

Code: Alles auswählen

return gethostbyname($hostname) != $hostname;
immer wahr ist. Außerdem wäre es gut, statt:

Code: Alles auswählen

$cApiClient->getProperty("dnsbls", "servers")

Code: Alles auswählen

getEffectiveSetting("dnsbls", "servers")
, damit auch Systemeinstellungen greifen.

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Do 11. Okt 2007, 10:28

Ist teilweise korrekt:

Das mit der Reihenfolge ist mir auch schon aufgefallen, hatte ich auch bereits im obigen Beitrag korrigiert, sorry. Aber die http:BL gibt nicht immer ein Ergebnis zurück, sondern nur bei IPs die bereits auffällig geworden sind.

Allerdings hab ich das Modul bereits erweitert, weil die IPs aus der Datenbank dort nicht verschwinden, sodass selbst ich gestern eine IP von meinem ISP dynamisch zugewiesen bekam, über die vor 190 Tagen *eine* Spam-Mail versendet worden war und ich deshalb als Spammer klassifiziert wurde.

Die Erweiterung bezieht sich also auf eine Prüfung des Ergebnisses gegen einen regulären Ausdruck. Hier erstmal der aktuelle Code

Code: Alles auswählen

<?php
if (!($edit || $contenido)) {
	cInclude("classes", "class.user.php");
	
	$clientAdmins = User::getClientAdmins($client);
	
	$cfg["tab"]["spamcache"] = $cfg['sql']['sqlprefix'] . "_spamcache";
	$sql = "CREATE TABLE IF NOT EXISTS `" . $cfg["tab"]["spamcache"] . "` (`ip` VARCHAR(15) NOT NULL, `dnsbl` TINYTEXT NOT NULL DEFAULT '', `lastaccessed` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`ip`))";
	$db->query($sql);
	
	$sql = "DELETE FROM `" . $cfg["tab"]["spamcache"] . "` WHERE ROUND((NOW() - `lastaccessed`) / 60) >= 5";
	$db->query($sql);
	
	$sql = "SELECT * FROM `" . $cfg["tab"]["spamcache"] . "` WHERE `ip`='$_SERVER[REMOTE_ADDR]'";
	$db->query($sql);
	if ($db->next_record()) {
		$dnsbl = $db->f('dnsbl');
		$sql = "UPDATE `" . $cfg["tab"]["spamcache"] . "` SET `lastaccessed`=NOW() WHERE `ip`='$_SERVER[REMOTE_ADDR]'";
		$db->query($sql);
		
		if ($dnsbl)
			dieSpammer($dnsbl);
	} else {
		$dnsbls = explode(",", getEffectiveSetting("dnsbls", "servers"));
		if (is_array($dnsbls)) foreach ($dnsbls as $dnsbl) {
			if (!$dnsbl) continue;
			
			$prefix = getEffectiveSetting($dnsbl, "prefix");
			if ($prefix) $prefix = ".$prefix";
			
			$regexp = stripslashes(getEffectiveSetting($dnsbl, "regexp"));
			
			if (isSpammer("$_SERVER[REMOTE_ADDR]$prefix", $dnsbl, $regexp)) {
				$sql = "INSERT INTO `" . $cfg["tab"]["spamcache"] . "`(`ip`, `dnsbl`) VALUES('$_SERVER[REMOTE_ADDR]', '$dnsbl')";
				$db->query($sql);
				dieSpammer($dnsbl);
			}
		}
		
		$sql = "REPLACE INTO `" . $cfg["tab"]["spamcache"] . "`(`ip`) VALUES('$_SERVER[REMOTE_ADDR]')";
		$db->query($sql);
	}
}
	
function isSpammer($ip, $dnsbl, $regexp = false) {
	if (!function_exists("gethostbyname"))
		return false;
		
	if (!preg_match('/(?:(?:[1-9]|[1-9][0-9]|1[0-9]{2,2}|2[0-4][0-9]|25[0-5])\.){4,4}/', "$ip."))
		return false;

	$hostname = join(".", array_reverse(explode(".", $ip))) . ".$dnsbl";

	return ((($result = gethostbyname($hostname)) != $hostname) && (!$regexp || preg_match("/$regexp/i", $result)));
}

function dieSpammer($dnsbl) {
	global $clientAdmins;

	header("HTTP/1.0 403 Forbidden");
	header("Content-type: text/plain");

	if (is_array($clientAdmins))
		$admin = array_shift($clientAdmins);
		
	die(sprintf("Sorry!\n\n%s says you are a spammer and we don't want spammers to use our services!\n" . (@$admin["email"] ? sprintf("If you think this is incorrect, please drop us a line via email to: %s.\n", str_replace("@", "(AT)", $admin["email"])) : '') . "\nThanks a lot.", $dnsbl));
}
?>
Danke für die Ergänzung mit getEffectiveSetting, Dodger.

Also, Einstellung wie folgt: In den Mandanten (oder jetzt auch Systemeinstellungen :-) ) wie gehabt

dnsbls
servers
<Server-Liste>

einfügen, um dem Modul mitzuteilen, gegen welche Server geprüft werden sollen. Die Zugangsschlüssel (wie im Falle Project Honey Pot) bitte (ACHTUNG: Änderung) als

<server>
prefix
<schlüssel>

anlegen. Soll eine Prüfung gegen einen RegEx erfolgen, den Ausdruck bitte als

<server>
regexp
<ausdruck>

hinzufügen.

Beispiel:

Code: Alles auswählen

Typ: dnsbls
Name: servers
Wert: dnsbl.httpbl.org

Typ: dnsbl.httpbl.org
Name: prefix
Wert: zugangsschlüssel1

Typ: dnsbl.httpbl.org
Name: regexp
Wert: ^127\.(((0|[0-9]|1[0-4])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([2-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])))\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
In diesem Beispiel verweigert das Modul den Zugriff auf die Seite, wenn die aufrufende IP von dnsbl.httbl.org als Spammer identifiziert wurde und die letzte "Spam-Aktion" nicht mehr als 14 Tage her ist oder die IP-Adresse über 20 Punkte bekommen hat (0 = ungefährlich, 255 = extrem gefährlich).

Bei uns funktioniert's bisher wunderbar.

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Do 11. Okt 2007, 14:02

Was mir noch aufgefallen ist:

Als RegEx sollte man besser

Code: Alles auswählen

^127\.(((0|[0-9]|1[0-4])\.([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9]{1,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([2-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])))\.([1-9]|[0-9]{2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
verwenden, sonst sperrt man den Googlebot aus. :oops:

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Mi 12. Dez 2007, 11:59

double hat geschrieben:Gude delinquent,
ich wollte mal bezüglich IP-Filter mal was fragen.
Bei mandanteneinstellung werden doch der Servername und prefix und regexp eingefügt.
bei prefix kommt login name aber wo wird der pw eingefügt?
Der prefix ist das, was bei einer DNS Abfrage der zu überprüfenden IP-Adresse vorangestellt wird. Im Falle von Project Honey Pot entspricht genau dies dem "Passwort".

Benutzt Du den Project Honey Pot Dienst?

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 12:44

Benutzt Du den Project Honey Pot Dienst?
Ja, ich habe dein Modul genommen und mich bei,

http://www.projecthoneypot.org/ ,

angemeldet. Nun habe ich bei der Mandanteneinstellung (die Einstellungen) wie deine Übernommen.

Siehe hier:
Typ: dnsbls
Name: servers
Wert: dnsbl.httpbl.org --> Bis hier ist klar

Typ: dnsbl.httpbl.org
Name: prefix
Wert: zugangsschlüssel1 --> Kommt hier der Passwort rein, wenn ja wo der loginname?

Typ: dnsbl.httpbl.org
Name: regexp
Wert:.... Das habe ich übernommen!

Danke für deine Hilfe

Gruß double

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Mi 12. Dez 2007, 12:48

Jetzt habe ich Dein Problem verstanden. Du musst nicht Benutzername und Passwort von Project Honey Pot dort eingeben, sondern einen Zugangsschlüssel.

Wenn Du Dich auf projecthoneypot.org einloggst und anschließend auf
"Services" -> "HTTP Blacklist" klickst, steht dort in einem grauen Kasten "Your http:BL Access Key". Den dort angegebenen 12-Zeichen langen Schlüssel trägst Du bei Prefix ein.

Solltest Du diesen Kasten dort nicht sehen, kann es sein, dass Du Dich für die Blackliste nochmal separat anmelden musst. Einfach ein wenig klicken. Dann findest Du schon zum Ziel.

Gruß,
Manueul

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 12:51

Ach danke ich schaue mir mal das gleich an.


Gruß double

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 12:59

Jetzt habe ich die Mandanteneinstellung richtig gemacht denke ich.
Den Zugangs-Code habe ich bei prefix reingehauen.
Jetzt noch ein paar Fragen wenn Du nichts dagegen hast auf dein Modul:

Wenn ich bei der con_spamcache reinsehe, bekomme ich mom. nur meine IP-Adresse angezeigt. Sie wird nach 5 Minuten gelöscht nehme ich an. Meine Frage ist, wo wird meine IP-Adresse mit der Server-Blacklist verglichen?

Zeilennummer ?

Oder habe ich den vorgang falsch verstanden?

Es wäre nett wenn Du einpaar Worte über den Vorgang schreiben würdest!

Nochmals Danke und schönen Gruß

double

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Mi 12. Dez 2007, 13:34

Der Vorgang ist der folgende:

1. Besucher kommt auf deine Seite
2. Modul löscht in der Tabelle alle IP-Adressen, dessen Seitenaufruf länger als 5 Minuten her ist (Zeile 12)
3. Modul sucht in der Tabelle nach der IP-Adresse des Besuchers (Zeile 15)

4a. IP Adresse des Besuchers ist dort vorhanden (Zeile 16)
5a. Zeitstempel aktualisieren (Zeile 19)
6a. Das Modul überprüft, ob diese IP-Adresse dort als Spammer hinterlegt ist, falls ja wird der Seitenzugriff verweigert, falls nein, wird der Zugriff gewährt. (Zeilen 21 und 22)

4b. IP Adresse des Besuchers ist dort nicht vorhanden (Zeile 23)
5b. Das Modul konnektiert nacheinander zu allen angegebenen Servern (Zeile 25) mit den Zugangsschlüsseln und fragt dort, ob die IP Adresse als Spammer identifiziert wurde (Zeile 33). Die Rückgabe von diesem Server wird gegen den RegEx überprüft. (Zeile 52) Sobald dieser Vorgang ergibt, dass es sich um einen Spammer handelt wird dies in der Datenbank gespeichert (Zeile 35) und der Zugriff auf die Seite verweigert (Zeile 36). Falls es sich für diesen Server nicht um einen Spammer handelt, wird der nächste gefragt (Zeile 38). Wenn keiner der Server den Besucher "als Spammer identifizieren" wird dies ebenfalls in der DB gespeichert (Zeile 41) und der Zugriff gewährt.

Ich hoffe das reicht :-)

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 14:10

delinquent hat geschrieben:
Ich hoffe das reicht :-)

Ja, das reicht, danke nochmals.

Jetzt mal eine doofe Frage:

Aber wie kann ich jetzt wirklich feststellen das es funktioniert?

Ich habe mal eine gesperrte Ip-Adresse mitgegeben:

Code: Alles auswählen

$ip_test = "124.120.36.84 ";
isSpammer("$ip_test$prefix" , $dnsbl, $regexp) --> liefert false?
Oder mache ich da ein Denkfehler?

Gruß double

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 14:22

Sorry,

habe es nochmal getestet und es funktioniert tatsächlich :oops: :oops:


Also was ich noch gerne wissen wollte ist das hier:

Code: Alles auswählen

 $dnsbl = $db->f('dnsbl');  //?

Gruß double

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Mi 12. Dez 2007, 14:32

Das ruft aus der DB ab ob und welcher Server eine IP Adresse als Spammer identifiziert hat.

bilal.arslan_4fb
Beiträge: 35
Registriert: Do 23. Aug 2007, 08:36
Kontaktdaten:

Beitrag von bilal.arslan_4fb » Mi 12. Dez 2007, 15:05

Die Fehler Meldung falls eine gesperrte IP erkannt wird kommt wie folgt:

Code: Alles auswählen

Warning: Cannot modify header information - headers already sent by (output started at
Contenido kann keine Header mehr setzen weil es die schon gibt.

Wäre es nicht besser bei

Code: Alles auswählen

function dieSpammer($dnsbl) {
   global $clientAdmins;

   header("HTTP/1.0 403 Forbidden");
   header("Content-type: text/plain");

   if (is_array($clientAdmins))
      $admin = array_shift($clientAdmins);
      
   die(sprintf("Sorry!\n\n%s says you are a spammer and we don't want spammers to use our services!\n" . (@$admin["email"] ? sprintf("If you think this is incorrect, please drop us a line via email to: %s.\n", str_replace("@", "(AT)", $admin["email"])) : '') . "\nThanks a lot.", $dnsbl));
}
die beiden headers raus zu nehmen?
Du machst doch eh die() das reicht doch?

Gruß double

delinquent
Beiträge: 184
Registriert: Fr 17. Aug 2007, 12:15
Kontaktdaten:

Beitrag von delinquent » Mi 12. Dez 2007, 18:47

Nicht empfehlenswert, weil dadurch die Meta-Information, dass der Zugriff verweigert wurde verloren geht.

Hast Du das Modul im aller ersten Container deines Layouts ohne Leerzeichen vor <container id="..."...>?

Gesperrt