Zielsetzung
Mit BreezingForms im EasyMode/QuickMode erstellte Formulare (z.B. Kontaktformular) lassen sich mit wenigen einfachen Mitteln gegen Spambots halbwegs schützen. Der Schutz ist nicht hundertprozentig, aber doch immerhin so gut, dass man gut 95% Spam abblockt. Wie viele andere vertrete auch ich die Auffassung, dass das Prinzip der alten einfachen Captcha-Grafiken nicht zuverlässig genug ist, um dafür in Kauf zu nehmen die User mit den komplizierten Gekritzel der Grafiken zu belästigen. Leider gilt durch mittlerweile KI-unterstützte Allgorithmen auch die reCAPTCHA-Grafik von Google nicht mehr als zuverlässig. Weiterhin hat die reCAPTCHA-Technik von Google einen großen Nachteil: Sie steht im Konflikt mit der europäischen Datenschutzgrundverordnung. Der Grund ist einfach folgender. Schon beim Aufruf einer Seite mit einem Formular in dem dieser Spamschutz integriert ist, wird die Grafik und diverse Scripte von Google-Server geladen. So eine extern geladene Ressource hat immer den Effekt, dass beim Laden von diesen Fremdservern an diesen die aktuelle IP-Adresse des Seitennutzer übermittelt werden muss. Da das im Falle des Google-Servers i.d.R. auf amerikanischen Servern passiert, ist hier immer die Frage nicht hundertprozentig beanwortet, was mit diese personenbezogenen Daten in Fragen Speicherung und Analyse passiert - trotz aller Zusicherung von Google.
Es gibt auch weitere gute Technologie wie HoneyPot und Akismet. Leider sind diese immer mit einem Account verbunden und auch hierbei werden Daten zu externen Server übertragen - und dazu mal eben die kompletten Formularinhalte. Vorteil: zumindest werden die Daten erst beim Absenden des Formulars zwecks Prüfung versendet, so dass man vorher eine Chance hatte den Nutzer DSGVO-konform darüber aufzuklären. Aber bei gewerblichen genutzten Websites sind die genannten Accounts auch kostenpflichtig.
Deshalb greife ich in der Umsetzung auf die Kombination von 2 Schutzmechanismen zurück, die sich bewehrt haben.
Diese Anleitung basiert auf den Einsatz von Joomla 1.5.26 bis 3.8.10 und BreezingForms 1.9 im EasyMode oder QuickMode, kann aber sicher auch auf spätere Versionen adäquat angewendet werden.
Als Schutzmethoden kommen kombiniert zum Einsatz:
- der Zeitfensterschutz
- eine einfache Honey-Pot-Lösung
- und ein Bad-Words-Check
Zeitfensterschutz
Das Prinzip
Das Prinzip dieser Spamprüfung liegt darin, die Zeit auszuwerten zwischen dem Aufruf der Formularseite und dem Absenden. Kein Mensch füllt ein Formular unter 10 Sec. aus, und braucht sicher auch nicht länger als 20 Min. Also fragen wir genau dieses Zeitfenster ab.
1. Verstecktes Formularfeld
Wir erzeugen ein unsichtbares Formularfeld in unserem Formular. Das Verstecken kann über ein hidden-Input erfolgen, besser jedoch ist es über CSS-Attribute in einer externen CSS-Datei zu arbeiten und somit ein weniger verdächtiges normales Text-Input-Feld zu verwenden.
Um das Feld über die ID zu adressieren, verwenden wir die FF-Feld-ID (In der PF-Element-Verwaltung ist das der Feld-Name). Es wird also ein ganz normales Textfeld im Formular oder Verstecktes Eingabe-Feld eingebaut. z.B. mit dem Bezeichner check oder tooken. Gern kann auch eine Feldname verwendet werden, der nicht gleich so offensichtlich darauf hindeutet, dass dieses zu Prüfzwecken bestimmt ist.
2. Zeitwert übergeben
Das Feld müssen wir mit einem Zeitwert vorbefüllen. Damit das Formular beim Aufruf diese Information im Feld tooken stehen hat, kann man ein eigenes kleines BF-Script erstellen (s. Scripte verwalten) und über die Formulareinstellung > Scripte > als Initialisierungsskript einbinden. Entweder als Bibliotheksskript oder als "Spezial"-Skript.
Hier die beispielhaften Optionen für das neu anzulegende PF-Script:
- Titel: checktooken
- Veröffentlicht: ja
- Paket: FF
- Name: ff_checktooken
- Tpy: Element Initialisierung
- Beschreibung: Dieses Feld ist ein Formular-Ausfülldauer-Prüffeld.
Wenn die Zeitspanne zw. Formularaufruf und Formularsenden zu klein oder zu groß ist,
dann wird es nicht verarbeitet, weil vermutlich ein Spam-Versuch.
Das ist der Code-Inhalt dieses Initialisierungsscriptes:
function ff_checktooken() {
$TimeFaktor = 65;
return "ff_getElementByName('tooken').value = '".( time() * $TimeFaktor)."';\n";
}
Die Multiplikation mit 65 dient lediglich nur noch mal als kleine Form der Zeit-Verschlüsselung, auch das, damit es nicht gleich so einfach für Hacker nachvollziehbar ist.
3. Prüfen beim Absenden
Nun müssen wir beim Absenden des Formulars abfragen, ob die aktuelle Zeit im Zeitfenster zw. unserer übermittelten Aufrufzeit und den Minimal- und Maximalwerten liegt.
Es gibt zwei Möglichkeiten diese Prüfung zu integrieren - entweder man macht es im Formular über ein Custom-Script, oder man Erstellt in der BF-Teile-Verwaltung ein php-Script-Schnippsel welches dann im Formular als Bibliotheks-Script eingebunden wird. Ich bevorzug die 2. Variante, weil ich das Script das bei mehreren Formularen immer wieder aus der Bibliothek heraus einbinden kann. Da spart Pflegeaufwand.
Wir gehen unter Teile verwalten her und erzeugen ein neues PHP-Script mit foldenen Inhalten:
- Titel: checkSpam
- Veröffentlicht: ja
- Paket: FF
- Name: ff_spamcheck
- Typ: Beginn Übermittlung
- Beschreibung: Hiermit wird der gesendete Formularinhalt auf Spam-Kriterien geprüft:
Check 1: Das Feld Tooken enthält den Zeitstempel beim Formularaufruf (per JS erzeugt).
Dieser Zeitstempel wird verglichen mit dem des Sendens/Empfangs des Formulars.
Wenn die Spanne zu klein (10s) oder zu groß ist, wird erfolgt eine Weiterleitung auf die HomePage ohne Formularverarbeitung.
Die Teilung durch 65 ist ein willkürlicher Wert zur einfachen Verschlüsselung des Zeitstempels.
Check 2: Das HoneyPot-Feld "SchmeissFliegNFalle" muss leer sein.
In das Feld Code kommt dann dieses Script:
$this->execPieceByName('ff_InitLib'); // Prüfung der Zeitspanne zw. Formular Aufruf und Absenden $tooken = ff_getSubmit('tooken'); if ( empty($tooken) ) { // ff_die("checkSpam tooken empty" ); ff_die("Vielen Dank (1)!" ); } else { $FormCallTooken = intval($tooken); $TimeFaktor = 65; $CallTime = $FormCallTooken / $TimeFaktor; $now = new DateTime("now"); $secondsDiff = time() - $CallTime; if ( $secondsDiff < 20 || $secondsDiff > 2000 ) { // if (headers_sent() === false) header('Location: /index.php', true, 302); // ff_die("checkSpam checkSecDiff: ". $CallTime .' '. $secondsDiff .' zu '.time()); ff_die("Vielen Dank (2)!" ); } }
Zu Debug-Zwecken gebe ich mit ff_die() einen Text aus, der mir an der Nummern sagt, warum die Formular-Datenverarbeitung abgebrochen wurde. Der Spammer bekommt aber ein freundliches "Vielen Dank".
Honey-Pot
Das Prinzip
Das Prinzip von Honey Pot ist, dass ein für den Seitenbesucher unsichtbares Formularfeld leer gesendet werden muss. Spambots haben das Bestreben in alle gescannten Felder auch etwas einzutragen zu wollen. Der Nutzer, der dieses Feld nicht sieht, trägt nichts ein, der Bot schon.
Wir brauchen drei Bestandteile.
- Ein neues Formularfeld,
- ein Script, welches dieses Feld vor den Seitenbesucher versteckt
- und eine Prüfscript, welches die Formuardatenverarbeitung abbricht, wenn es feststellt, dass in desem HoneyPot-Feld Inhalt übergeben wurde.
1. Neues Eingabefeld
Wir beginnen zwar mit dem Eingabefeldanlegen, kommen dann aber noch mal auf die Konfiguration des Feldes zurück.
Wir legen also zunächst ein ganz normales Input-Feld im Formular an. Dieses bekommt von mir üblich den Feldnamen Schmeissfliegenfalle. Als Titel/Label sollte man einen Namen wählen, der verlockend klinkt, wie "Your Message" oder "Your website addres". Evtl. kann man auch den Feldname noch so verlockend benennen. In folgenden Scriptbeipiel gehe ich davon aus, dass es SchmeissFliegenFalle heißt.
2. Feld verstecken
Das Verstecken mache ich nicht mit einfachen CSS-Scripten, nicht inline und auch nicht extern - das wäre zu offensichtlich versteckt. Statt dessen habe ich dafür ein JavaScript gebaut, welches die zum Verstecken nötigen Styles zweiversetzt einfügt. Das Feld ist also auch für Nutzer kurzzeitig sichtbar und verschwindet erst dann. Als Idee könnte man noch eleganter das Feld erst verstecken, wenn der Nutzer draufklickt. Dazu nutze ich wieder die BF-Bordmittel. Es wird also wieder über die Scripte-Verwaltung ein Script angelegt:
- Titel: verstecke SchmeissFliegenFalle
- Veröffentlicht: ja
- Paket: FF
- Name: ff_versteckschmeissfliegenfalle
- Typ: Element Initialisierung
- Beschreibung: Macht das Schmeißfliegenfalle-Formularfeld unsichtbar für Benutzer damit diese es nicht ausfüllen
Der Code des Scriptes sieht bei mir dann i.d.R. so aus:
function ff_versteckschmeissfliegenfalle(element,condition) { var Fld = jQuery(element); Fld.parent().parent().parent().delay(5000).css({'height':'0', 'overflow':'hidden', 'margin':0}); }
Achtung: Ich nutze jQuery. Wer in seinem Joomla-Projekt nicht jquery nutzt, wird sicher Wege finden dies auch pur mit JS hinzubekommen. Beachten Sie auch, dass Feld so zu verstecken, dass es nicht unsichtbar aber existierend über einem anderen sichtbaren Feld liegt und damit im überlagerten Feld eine Eingabe unmöglich macht, weil diese Feld dann den Focus nicht erhalten kann. Da passiert i.d.R. sehr schnell wenn das HoneyPot-Feld mitten im Formular liegt und nicht als erstes Feld im Formular eingebunden ist.
Zum Verstecken gibt es ja reichlich Möglichkeiten. Ich nutze die Höhenangabe 0 in Verbindung mit overflow-hidden. Man kann auch eine absolute Positionierung im unsichtbaren Seitenbereich machen oder das Feld unter ein anderes legen ... Jeder wie er Lust hat.
Schließlich, damit das Verstecken-Script auch in Verbindung mit dem HoneyPot-Feld ausgeführt wird, müssen Sie dem Feld noch dieses Script anbinden. Dazu geht man erneut in die dieses Formular-Element über die PF-Formular-Verwaltung. Für dieses Feld finden Sie einen Registerreiter "Erweitert" den Sie anklicken. Ganz unten gibt es die Gruppe "Initialisierungsskript". Hier wählen wir unser Bibliothek-Script 'FF:ff_versteckschmeissfliegenfalle' und aktivieren die Checkboxen "Formulareintritt" und "Seiteneintritt".
3. Prüfen beim Absenden
Angleicher Stelle, wie oben, wo wir das Zeitfenster abfragen, ergänzen wir das Übermittlungsscript durch die Prüfung, ob der Feldinhalt leer geblieben ist. D.h. an das checkSpam-Script oben fügen Sie folgende Zeilen an:
// Prüfung Feld muss leer sein (HoneyPot) $HoneyPotVal = ff_getSubmit('SchmeissFliegNFalle'); if ( $HoneyPotVal != '') { // if (headers_sent() === false) header('Location: /index.php', true, 302); // ff_die(__FUNCTION__." HoneyPot filled" ); ff_die("Vielen Dank (3)!" ); }
Der Grund warum ich diese Scriptteile in einem Script zusammenfasse, ist der: Sie können im Formular bei der Anbindung nur ein Bibliotheksscript zuweisen. Wenn Sie also mehrere Prüfung machen wollen, müssen alle in einem Script durchgeführt werden. Man kann aber auch mehrere einzelen Scriptteile in der Teileverwaltung ablegen, die lediglich Funktionen enthalten. Im Formular selbst wählt man dann nicht das eine Bilbliotheksscript aus, sondern erstellt dort an Ort und Stelle eine Custom-Script, welches die Funktionen aus den Teilen wahlweise aufruft.
Bad-Words-Check
Als dritten Baustein der Formular-Spam-Prüfung kann man Feldinhalte auf enthaltene Worte durchsuchen, die auf Spam hindeuten. Typisch für Spamnachrichten sind Verweise auf Webadressen oder diverse erotisch beladene Reizworte oder Casino etc.
Der Aufwand hiefür ist einfach. Wir ergänzen unser Prüftscript durch einen weitere Check-Codesequenz:
// Prüfung ob Felder Bad-Words enthalten $badWordsArr = array("http","www",".ru","My name is","visit", "your website"); $checkFieldsArr = array('KontaktformularNachricht'); foreach ($checkFieldsArr as $Fld) { $FldVal = ff_getSubmit($Fld); $matches = array(); $matchFound = preg_match_all( "/\b(" . implode($badWordsArr,"|") . ")\b/i", $FldVal, $matches ); if ($matchFound) { ff_die("Vielen Dank (4)!"); } }
Die Bad-Wordliste kann beliebig ausgefüllt werden. Ebenso die Liste mit den zu prüfenden Feldern im Array $checkFieldsArr. Nachteil dieser Methode ist derzeit, dass die hier fest codierten Feldnamen verhindern, dass dieses Script-Teil nicht auf beliebige Formulare angewendet werden kann, sondern nur auf die wo es die genannten Felder auch gibt. Für Projekte in denen es nur eine Formular gibt, ist diese Lösung vermutlich ausreichend.
Anbinden der Prüfung an das Formular
Nun fehlt noch einer kleiner aber wichtiger Schritt. Damit beim Emfang der Formulardaten durch BreezingForms unser Formular auf Spam geprüft wird, muss o.g. Prüfscript an das Formular angebunden werden. Dazu gehen wir in der Formular-Verwaltung in das Formular. Unter dem Registerreiter "Erweitert" finden wir eine etwas versteckten Button "Mehr Optionen". Über diesen Button gelangen wir die Formulareinstellungen. Dort haben wir einen Register "Übermittlungsteile" mit der Gruppe "Beginn Übermittlung". Dort wählen wir unser Bibliotheksscript "FF::ff_checkSpam" aus und speichern abschließend die Formulareinstellungen.
Jetzt kann das Formular getestet werden.