heinetz: Spamschutz ohne Captcha

Hallo Forum,

wir haben hier ein Formular gefunden, über das der User a.d. Website Mails versenden kann. Das Formular hat ein Skript entdeckt, dass nun damit anfängt, Spam zu versenden. Jetzt sind Massnahmen gefragt, um das zu verhindern. Captchas finde ich aber doof ;) … und habe in der Vergangenheit auch schonmal Lösungen gebaut, die ohne auskommen. Bspw. mit einem versteckten Feld, dass nicht ausgefüllt werden darf.

Wir würdet ihr so etwas elegant und barrierefrei lösen?

gruss, heinetz

  1. wir haben hier ein Formular gefunden, über das der User a.d. Website Mails versenden kann. Das Formular hat ein Skript entdeckt, dass nun damit anfängt, Spam zu versenden.

    Definiere "elegant".

    Ich habe kaum Probleme mit Spam aus meinem Formular.

    Kontaktformular wird von Suchmaschinen ignoriert, aber in der robots.txt nicht konkret genannt. Es hat bewusst auch keinen der klassischen Namen.

    Ich habe einen, eigentlich recht primitiven aber ergänzungsfähigen und über die Jahre optimierten Spamfilter, der die Nachricht untersucht:

    <?php
    function ftx_is_spam($str) {
    
    	# Muster: Eintrag beginnt mit Link:
    	$arMuster[]='^http:\/\/';
    	$arMuster[]='^<a href';
    	$arMuster[]='^\[url=';
    	#3 Links:
    	$arMuster[]='http:\/\/.*http:\/\/.*http:\/\/';
    	
    	#5 Sonderzeichen aufeinander
    	$arMuster[]='&#[0-9A-F]{2,3};&#[0-9A-F]{2,3};&#[0-9A-F]{2,3};&#[0-9A-F]{2,3};&#[0-9A-F]{2,3};';
    	
    	#Spam-Begriffe
    	$arMuster[]='tramadol]';
    	$arMuster[]='viagra';
    	$arMuster[]='cialis';
    	$arMuster[]='prozac';
    	$arMuster[]='pharmacy';
    	$arMuster[]='fluotextine';
    	$arMuster[]='charts';
    	$arMuster[]='investing';
    	$arMuster[]='cheap';
    	$arMuster[]='sacher[- ]finanz';
    	$arMuster[]='thepowerlevel.com';
    	$arMuster[]='well-racking.com';
    	$arMuster[]='hcracking.com';
    	$arMuster[]='doxyciline';
    	$arMuster[]='investment';
    	$arMuster[]='trading';
    	$arMuster[]='profit';
    	$arMuster[]='dollars';
    	$arMuster[]='farming';
    	$arMuster[]='watches';
    	$arMuster[]='replica';
    	$arMuster[]='gucci';
    	$arMuster[]='click here';
    	$arMuster[]='stock price';
    	$arMuster[]='trading software';
    	$arMuster[]='your profit is fully maximized';
    
    	# excec!
    		
    	$replace['i']='########I#######';
    	$replace['l']='########I#######';
    	$replace['o']='########O#######';
    	$replace['a']='########A#######';
    	$replace['c']='########C#######';
    	$replace['z']='########C#######';
    	
    	$replace['########I#######']='[il1]';
    	$replace['########O#######']='[o0]';
    	$replace['########A#######']='[a@]';
    	$replace['########C#######']='[czxs]';
    	
            $str=trim(strtolower($str));
            $arKeys=array_keys($replace);
    	
    	foreach ($arMuster as $strMuster) {
    	        foreach ($arKeys as $key) {
    	             $strMuster=str_replace($key, $replace[$key], $strMuster);
    	        }
    	        #echo "preg_match('/'$strMuster'\/', $str)\n";
    		if (preg_match('/'.$strMuster.'/',  $str)) { ftx_SpamdetectDie(); return true; }
    	}
    	return false;
    }
    
    function ftx_SpamdetectDie() {
    	header("HTTP/1.0 403 Forbidden",true,403);
    	die('
    <html style="margin:20%">
    	<h1>Spam erkannt!</h1>
    	<p>Falls Sie nicht zu spammen versuchten: Gehen Sie zur&uuml;ck, geben Sie weniger Urls ein, vermeiden Sie Begriffe wie Viagra etc.<p>
    	<p><a href="http://translate.google.de/#de/en/Spam%20erkannt!%0A%0AFalls%20Sie%20nicht%20zu%20spammen%20versuchten%3A%20Gehen%20Sie%20zur%C3%BCck%2C%20geben%20Sie%20weniger%20Urls%20ein%2C%20vermeiden%20Sie%20Begriffe%20wie%20viagra%20etc.">Translate this for me.</a></a>
    </html>
    ');
    exit;
    }
    
    1. Die regelmäßig primitiven Bots können weder JS noch CSS. Die füllen auch "blinde" Felder aus. Das kann man ausnutzen in dem man $_POST['hier_darf_nichts_stehen'] auf Inhalt untersucht.

    Das genügt Denn die Spammer wollen ja kein Geld ausgeben. Die suchen sich, weil das billiger ist, Idioten, die den Stuff aus Matt's Skript-Archiv benutzen.

    Jörg Reinholz

  2. Hallo,

    ich mag auch keine Captchas und konnte gute Ergebnisse erzielen mit der Kombination folgender Maßnahmen: a) dem von dir schon angesprochenen Honeypot (der wurde aber schnell geknackt) b) dem Einbau einer ID (mittels php's microtime() ) welche als hidden field gesendet wird aber auch als Session ID gespeichert wird. Das Formular wird nur evaluiert, wenn diese übereinstimmen. Hintergrund ist das viele Bots das Formular nicht auf Deiner Seite aufrufen sondern vielmehr direkt einen Postrequest auf die Zielseite starten. Daher wird bei Bots die php-Sessionvariable nicht korrekt gesetzt. c) zwischen Formularaufruf und -evaluierung müssen mindestens 3 Sek. vergehen. Hintergrund ist: Menschen brauchen Zeit für das Ausfüllen, Maschinen nicht. Beim Loginformular verzichte ich auf diesen Spamschutz, da die Leute direkt Daten eingefügt bekommen (vom Browser) und es durchaus sein kann, dass das Abschicken innerhalb 0.5 Sek. geschieht.

    Cheers, Baba

    --
    Baba kommt von Basketball
    1. hello,

      b) dem Einbau einer ID (mittels php's microtime() ) welche als hidden field gesendet wird aber auch als Session ID gespeichert wird. Das Formular wird nur evaluiert, wenn diese übereinstimmen. Hintergrund ist das viele Bots das Formular nicht auf Deiner Seite aufrufen sondern vielmehr direkt einen Postrequest auf die Zielseite starten. Daher wird bei Bots die php-Sessionvariable nicht korrekt gesetzt.

      das Prinzip klingt einleuchtend: Beim POST-Request wird überprüft, ob der "User" vorher auf der Seite war, wo er ein Form ausgefüllt hat, also der Referrer. Wodurch unterscheidet sich deine Methode davon, einfach den Referrer abzufragen? Der Wert aus microtime ist der name des hidden-Field oder der value?

      In einem andren Ansatz, den ich gelesen hatte, werden die Namen der Formularfelder dynamisch generiert. Das erschien mir auch sinnig. Was hälst Du davon?

      c) zwischen Formularaufruf und -evaluierung müssen mindestens 3 Sek. vergehen. Hintergrund ist: Menschen brauchen Zeit für das Ausfüllen, Maschinen nicht. Beim Loginformular verzichte ich auf diesen Spamschutz, da die Leute direkt Daten eingefügt bekommen (vom Browser) und es durchaus sein kann, dass das Abschicken innerhalb 0.5 Sek. geschieht.

      Ja, gute und einfache Massnahme.

      gruss, heinetz

      1. das Prinzip klingt einleuchtend: Beim POST-Request wird überprüft, ob der "User" vorher auf der Seite war, wo er ein Form ausgefüllt hat, also der Referrer. Wodurch unterscheidet sich deine Methode davon, einfach den Referrer abzufragen?

        Der Referrer kann einfach manipuliert werden. Und das wird auch gemacht.

        Der Wert aus microtime ist der name des hidden-Field oder der value?

        Der value. e.g. if($_POST["formts"] != $_SESSION["formts"]) etc...

        In einem andren Ansatz, den ich gelesen hatte, werden die Namen der Formularfelder dynamisch generiert. Das erschien mir auch sinnig. Was hälst Du davon?

        Viel. Ich denke das kann sehr gute Ergebnisse bringen wenn alles andere versagt. Ich musste davon nur keinen Gebrauch mehr machen, da die Methoden schon wirksam waren.

        Cheers, Baba

        --
        Baba kommt von Basketball
        1. danke!

          Viel. Ich denke das kann sehr gute Ergebnisse bringen wenn alles andere versagt. Ich musste davon nur keinen Gebrauch mehr machen, da die Methoden schon wirksam waren.

          Mein Mix aus Massnahmen sieht jetzt so aus:

          1. honeypot Das als Eingabefeld Absender_Email, dass schon ein Spambot für sich entdeckt hatte, ist nun mit display:none nicht mehr zu sehen und darf für die Validierung auf keinen Fall ausgefüllt werden.

          2. $_POST["formts"] != $_SESSION["formts"] Entsprechend Deinem Tipp

          3. generierte name-Attribute Beim Rendern des HTML-Codes für das Formular werden die name-Attribute der Inputs aus md5(date('d').'fieldname) generiert. Beim generieren werden diese String in $_SESSION['fields'][] geschrieben. Bei der Validierung darf $_POST keine Felder enthalten die nicht in $_SESSION['fields'][] gefunden werden ... Im Grunde brauche ich dann die Honeypots sparen und das Formular darf nicht vor 00:00 ausgefüllt und nach 00:00 abgeschickt werden.

          Das dürfte so funktionieren, oder?

          gruss, heinetz

          1. Warum nicht... Allerdings sehe ich zwei Probleme bei datumsabhängigen Feldnamen. a) das Mitternachtsproblem und b) dass Du während 24h identische Feldnamen hast. Evtl. können sich die Bots zeitnah adaptieren.

            Warum nicht den Salt (in Deinem Fall date("d")) dynamisch kreieren (e.g. uniqid()) und diesen als hidden Field ebenfalls übergeben. So kannst Du die Feldnamen auf Evaluierungsseite ebenfalls korrekt generieren. Den Salt würde ich allerdings nicht neu generieren sondern übernehmen, für den Fall das der User das Formular überarbeitet (e.g. abgesendet hat und nun Feldeingaben überarbeiten muss), sonst wird es zu fitzelig.

            Cheers, Baba

            --
            Baba kommt von Basketball