Kommentarspam – mal wieder

Über Kommentarspam hatte ich mich ja schon mal geärgert und eine Browsererkennung implementiert. In den letzten Tagen waren aber wieder viele hundert Spamkommentare im Blog.

Haben meine Methoden versagt? Ein Blick in die Logfiles zeigen, daß von der betreffenden IP tatsächlich komplette Webseiten geholt werden. Offenbar ist da ein voll qualifizierter Browser am Werk.

Natürlich – das ist ja der Sinn dieses Tests – soll in einem Browser die Kommentarfunktion anstandslos funktionieren. Unglücklicherweise tut es das auch für den Spammer.

Die dümmsten Spammer haben die größten Erfolge

Was unterscheidet nun also den Spammer von einem normalen Nutzer? Auffällig ist die Schnelligkeit, mit der Kommentare versendet werden. Es liegen kaum zwei Sekunden zwischen zwei abgesendeten Kommentaren. Direkt nach dem Laden der Seite wird der Kommentar abgeschickt.

Ganz offensichtlich benutzt der Spammer einen gescripteten Browser. Allerdings waren die ersten Versuche noch sehr langsam. Vielleicht ist das ja die neue Generation von Spammern, die nicht einmal mehr programmieren kann, sondern mit einem Makrorekorder die Bedienung aufzeichnet, um sie dann tausendfach abzurollen.

Eine Möglichkeit besteht darin, den per Javascript nachgeladenen Code nicht direkt auszuführen, sondern per erste einige Sekunden verzögert zu aktivieren. Aber wo legt man da die Grenze? Und was ist mit Nutzern, die reloaden, bevor sie den Kommentar eintippen. Ich mache das öfter, um zu sehen, welche Kommentare noch zuletzt hinzu gekommen sind.

Eine andere Möglichkeit besteht darin, die Anzahl der Kommentare pro Zeiteinheit, also die Rate, auszuwerten. Dies geht nicht mehr im Formular, sondern benötigt einen Blick auf die Historie.

Diese Historie findet sich in der Datenbank. Also kann die Datenbank auch selbst das Rate-Limiting machen.

CREATE OR REPLACE RULE lutz_prevent_spam AS
  ON INSERT TO ezcomment
    WHERE 5 < (SELECT Count(ip) FROM ezcomment
                WHERE ip = NEW.ip
                  AND created > NEW.created - 60)
  DO INSTEAD
    DELETE FROM ezcomment
     WHERE ip = NEW.ip
       AND created > NEW.created - 60*10;

Es wird also geschaut, ob von dieser IP in den letzten 60 Sekunden mehr als fünf Kommentare abgesendet wurden. Ist das der Fall, werden alle Kommentare von dieser IP gelöscht, die in den letzten 10 Minuten geschrieben wurden.

Das scheint aktuell zu halten. Drückt mir die Daumen.

Einen Tag später

Wie in den Kommentaren zu sehen, hat es nicht gehalten. Jedenfalls nicht ganz richtig.

Die neue Lösung besteht darin eine neue Tabelle zu haben, die die blockierten IPs aufzählt:

       Table "public.ezcomment_block"
 Column |          Type          | Modifiers 
--------+------------------------+-----------
 ip     | character varying(100) | not null
 seen   | integer                | not null
Indexes:
    "ezcomment_block_pkey" PRIMARY KEY, btree (ip)
Rules:
    update_if_necessary AS
 ON INSERT TO ezcomment_block
   WHERE new.ip IN (SELECT b.ip FROM ezcomment_block b)
 DO INSTEAD  UPDATE ezcomment_block c SET seen = new.seen
  WHERE c.ip = new.ip

Mit der Regel, die die Inserts bei Bedarf in Updates umschreibt, kann man dauerhaft Inserts benutzen. Das vereinfacht die neue Regel für die Kommentare.

CREATE OR REPLACE RULE lutz_prevent_spam AS
 ON INSERT TO ezcomment
   WHERE 5 < (SELECT count(1) FROM ezcomment
          WHERE ip = new.ip AND created > new.created - 60)
      OR new.ip IN (SELECT ip FROM ezcomment_block
          WHERE seen > new.created - 24 * 60 * 60)
 DO INSTEAD (
  DELETE FROM ezcomment WHERE ip = new.ip AND created > new.created - 10 * 60;
  INSERT INTO ezcomment_block (ip, seen) VALUES (new.ip, new.created);
)

Mal sehen, wie lange das nun hält.

Avatar
Lutz Donnerhacke 03/06/2014 3:54 pm
Die beobachteten Reste sind echte Reste. Es ist das, was übrig bleibt, wenn gerade alles weg gelöscht wurde und noch ein paar Versuche nachkommen.

Ich muß also auch in die Zukunft löschen.
Avatar
Lutz Donnerhacke 03/06/2014 9:31 am
Wer auch immer der Spaßvogel ist. Er hat gestern abend 112 Versuche gemacht, von denen 20 liegen geblieben sind, weil sie knapp unter der Rate bleiben. Die Raten sind nun angepaßt.
Avatar
Lutz Donnerhacke 02/06/2014 5:27 pm
Die Kommentare werden nach dem Schema "Blog Post öffnen" .-> "Kommentar schreiben" -> "nächstes Blog Post öffnen" ... erzeugt. Sie landen also quer über alle Artikel. Erst danach geht die nächste Runde los.
Avatar
uwe 02/06/2014 5:12 pm
Unterscheiden sich die Spamkommentare denn im Inhalt? Vielleicht lohnt sich auch der Weg den neuen Kommentar mit bereits zu diesem Artikel zu vergleichen und nur wenn x% anders sind zuzulassen, Der schlechteste Weg wär in jedem Fall ein aptcha bei dem verzerrter unleserlicher Unsinn zu entziffern ist
Avatar
Lutz Donnerhacke 02/06/2014 3:11 pm
So sieht das dann aus

5.79.73.142 - - [02/Jun/2014:11:56:36 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:36 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:36 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:39 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:40 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:40 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:43 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:44 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:57 +0200] "POST /ger/comment/add HTTP/1.1"
5.79.73.142 - - [02/Jun/2014:11:56:59 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:56:59 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:01 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:09 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:10 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:10 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:12 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:14 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:32 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:33 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:37 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:38 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:39 +0200] "POST /ger/comment/add HTTP/1.1"
5.79.73.142 - - [02/Jun/2014:11:57:42 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:43 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:55 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:56 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:57:57 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:02 +0200] "POST /ger/comment/add HTTP/1.1"
5.79.73.142 - - [02/Jun/2014:11:58:06 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:06 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:08 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:10 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:17 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:21 +0200] "POST /comment/add HTTP/1.1" 302
5.79.73.142 - - [02/Jun/2014:11:58:33 +0200] "POST /comment/add HTTP/1.1" 302

Total 5 comments

Post a comment

Related content