==== 2.3 Typische Stolpersteine ====
* **POST wird zu GET:** Fehlt der abschließende Slash in der ''action''-URL, löst der Server einen Redirect aus, der POST in GET umwandelt.
* **Verwechslung Listen-ID / Subscribe-Page-ID:** unabhängige Zahlen aus unterschiedlichen phpList-Tabellen.
* **Fehlender Query-String in der Action:** muss ''?p=subscribe&id=SEITEN_ID'' enthalten.
===== 3. Honeypot-Schutz (Formular-Ebene) =====
* Verstecktes Zusatzfeld (hier: ''attribute3'', gemappt auf ein selbst angelegtes phpList-Attribut)
* Per CSS versteckt, von einfachen Bots erkennbar
* JavaScript prüft vor dem Absenden, ob das Feld befüllt ist
**Attribut anlegen:** Konfiguration → Attribute definieren → neues Text-Attribut.
**Auch auf der nativen Anmeldeseite verstecken** (Konfiguration → Anmeldeseiten → Custom-CSS-Bereich):
**Wichtige Einschränkung:** Ein Teil fortgeschrittener Bots lässt das Honeypot-Feld bewusst leer, um es zu umgehen. Der Honeypot allein reicht daher nicht als alleiniger Schutz (siehe Kapitel 7–9).
===== 4. Server-seitiger Zusatzschutz =====
==== 4.1 phpList-Konfiguration (config.php) ====
Teil der zentralen Konfigurationssammlung, siehe [[#config_snippets|Abschnitt config_snippets.php]] weiter unten, Block 1.
==== 4.2 BotBouncer-Plugin ====
Installierbar über Konfiguration → Plugins, Quelle: ''github.com/bramley/phplist-plugin-botbouncer''
* Prüft E-Mail-Adressen bei jeder Anmeldung gegen Stop Forum Spam
* Wichtige Einstellung: „Whether to validate email address submitted on the subscribe page" = Ja (gilt für externes und natives Formular)
==== 4.3 Hinweis zu Captcha-Plugins ====
Können zu Redirect-Problemen führen, wenn ein externes Formular ohne Captcha-Token an denselben Endpunkt postet wie die native Anmeldeseite.
===== 5. Automatisierte Spam-Meldung an Stop Forum Spam =====
==== 5.1 Konzept ====
Honeypot-Treffer werden automatisiert mit der echten, protokollierten IP an Stop Forum Spam gemeldet und in phpList gesperrt.
==== 5.2 Benötigte phpList-Attribute ====
^ Name (Beispiel) ^ Typ ^ Zweck ^
| Honeypot-Feld | Text | Verstecktes Fangfeld |
| „Als Spam bereits gemeldet" | Checkbox | Verhindert doppelte Meldung |
**Wichtig:** phpList speichert eine angehakte Checkbox als Wert ''on'' (nicht ''1'').
==== 5.3 Relevante Datenbank-Tabellen (phpList 3.x, Standardpräfix phplist_) ====
^ Tabelle ^ Zweck ^
| phplist_user_user | Haupttabelle der Abonnenten |
| phplist_user_user_attribute | Werte aller Attribute (userid, attributeid, value) |
| phplist_user_attribute | Attribut-Definitionen – keine Werte |
| phplist_user_user_history | Protokoll je Abonnent inkl. ip, date, summary |
Struktur vorab per ''SHOW TABLES;'' / ''DESCRIBE tabellenname;'' prüfen – kann je nach Version abweichen.
==== 5.4 Stop Forum Spam: Vorbereitung ====
- Account unter ''stopforumspam.com/signup'', API-Key unter „User Panel → Get API Key"
- Optionen „Public" und „Reportable" i. d. R. deaktiviert lassen
==== 5.5 Skript: spam_report.php ====
Außerhalb des Web-Roots ablegen bzw. per Serverregel gegen Web-Zugriff sperren.
{{page>gitea_code:phpList-Bot-Schutz:spam_report_php}}
=== 💬 Fragen & Feedback ===
Haben Sie Fehler gefunden, Verbesserungsvorschläge oder Fragen zu diesem Skript?
[[https://vw.falk.plus/JensFalk/phpList-Bot-Schutz/issues/new?labels=spam_report.php|Hier ein neues Gitea-Issue öffnen]]
==== 5.6 Typische Stolpersteine ====
^ Problem ^ Ursache ^ Lösung ^
| SQLSTATE[HY000] [2002] No such file or directory | PDO versucht Unix-Socket-Verbindung über localhost | ''$dbHost = '127.0.0.1' '' |
| Table 'db.xyz' doesn't exist | Tabellenpräfix nicht berücksichtigt | Präfix per SHOW TABLES; ermitteln |
| Falsche/verwechselte Attribut- bzw. Werte-Tabelle | phpList trennt Definitions- und Werte-Tabellen | Struktur vorher per DESCRIBE prüfen |
| IP Address ... cannot be added [XYZ-Infrastruktur] | Kein ip_addr übergeben → SFS nutzt automatisch die anfragende Server-IP, ggf. als geteilte Infrastruktur abgelehnt | Echte Bot-IP aus der History-Tabelle explizit mitschicken |
| SFS verlangt zusätzlich username | API-Anforderung | E-Mail zusätzlich als username mitschicken |
| Checkbox bleibt in Admin-UI leer trotz korrektem DB-Wert | phpList speichert on, nicht 1 | Insert-Wert auf 'on' setzen |
===== 6. Bounce-Verarbeitung (unzustellbare Adressen) =====
==== 6.1 Hintergrund ====
Viele Bot-Anmeldungen nutzen geratene, nicht existierende Adressen bei echten Organisationen. phpLists eingebautes Bounce-Management erkennt das automatisiert über ein IMAP/POP3-Postfach.
==== 6.2 Dediziertes Bounce-Postfach ====
* Eigene Adresse (z. B. ''bounces@nl.DEINE-DOMAIN.de''), getrennt vom persönlichen Postfach
* **Kein Spamfilter** (Bounce-Mails werden sonst fälschlich aussortiert, phpList braucht Zugriff auf alle eingehenden Mails)
* POP3 über SSL, „Nach Abruf löschen" aktiviert, damit das Postfach schlank bleibt
==== 6.3 Konfiguration (config.php) ====
Siehe [[#config_snippets|config_snippets.php]], Block 2.
==== 6.4 Fehlende native PHP-IMAP-Unterstützung ====
Ab PHP 8.4 ist die ''imap''-Erweiterung nicht mehr standardmäßig im PHP-Kern enthalten. Falls kein Root-Zugriff zur Nachinstallation besteht: Plugin **phplist-plugin-imap2** (''github.com/bramley/phplist-plugin-imap2'') installieren, das IMAP/POP3-Kommunikation in reinem PHP nachbildet.
==== 6.5 CLI-Aufruf vs. Remote-Call ====
Ein direkter CLI-Aufruf von ''processbounces.php'' kann an mehreren Hürden scheitern:
* Fehlender Init-Kontext (PHPLISTINIT nicht definiert) → Aufruf muss über index.php mit passenden Parametern erfolgen
* Datenbankverbindung im CLI-Kontext (bei eingeschränkten SSH-Umgebungen/Jails sieht die Shell u. U. nicht denselben MySQL-Socket wie der Webserver)
* Fehlende IMAP-Unterstützung (siehe 6.4)
**Praktikabler Weg, falls CLI nicht funktioniert:** Remote-Call über die Weboberfläche (dort läuft PHP-FPM, das über ein IMAP-Plugin funktionierenden Postfachzugriff haben kann, auch wenn CLI-PHP das nicht hat).
==== 6.6 Remote Processing Secret einrichten ====
phpList generiert das Secret standardmäßig bei jedem Aufruf neu, sofern kein fester Wert gesetzt ist – für Cron ungeeignet. Fester Wert nötig:
openssl rand -hex 20
**Wichtig:** Muss als ''$GLOBALS['config'][...]'' gesetzt werden, eine einfache Variable wird von ''getConfig()'' nicht erkannt (siehe [[#config_snippets|config_snippets.php]], Block 3).
==== 6.7 Remote-Aufruf-URL ====
https://nl.DEINE-DOMAIN.de/lists/admin/?page=processbounces&secret=DEIN_GENERIERTES_SECRET
===== 7. Erweiterte Bot-Erkennung: „Blitzbestätigung" und Adress-Scan =====
==== 7.1 Beobachtetes Muster ====
Manche Bots tragen geratene Namen bei echten Firmen-/Behörden-Domains ein, um herauszufinden, welche Adressen dort existieren (Directory-Harvest-Angriff über ein fremdes Formular als Umweg). Erkennbar an:
* Bounce-Mail bei ungültiger Adresse
* Ungewöhnlich schnelle Bestätigung nach der Anmeldung (z. B. 2–18 Sekunden)
==== 7.2 Wichtige Lektion: reine Zeitspanne als Kriterium reicht nicht aus ====
Eine erste Version des Erkennungsscripts filterte ausschließlich nach Zeitspanne (< 120 Sekunden zwischen Anmeldung und Bestätigung). Im praktischen Einsatz erfasste das dadurch fälschlich auch **echte, nur besonders schnell reagierende Menschen** (in einem dokumentierten Fall genügten einem echten Bekannten des Betreibers nur 35 Sekunden).
**Der entscheidende Unterschied:** Bei den fälschlich erfassten echten Personen war die IP-Adresse bei Anmeldung und Bestätigung identisch. Bei den tatsächlichen Bot-Fällen unterschieden sich die IPs dagegen durchgehend (unterschiedliche Maschinen im Botnetz für Anmeldung und automatisiertes Bestätigen).
==== 7.3 Verbessertes Kriterium: kurze Zeit UND unterschiedliche IP ====
**Benötigtes Attribut:**
^ Name (Beispiel) ^ Typ ^ Zweck ^
| „Blitzbestätigung — eher Bots" | Checkbox | Duplikatschutz |
**Skript: blitzbestaetigung_process.php**
{{page>gitea_code:phpList-Bot-Schutz:blitzbestaetigung_process_php}}
=== 💬 Fragen & Feedback ===
Haben Sie Fehler gefunden, Verbesserungsvorschläge oder Fragen zu diesem Skript?
[[https://vw.falk.plus/JensFalk/phpList-Bot-Schutz/issues/new?labels=blitzbestaetigung_process.php|Hier ein neues Gitea-Issue öffnen]]
==== 7.4 Wichtiger ethischer Punkt: bewusste Nichtmeldung der E-Mail-Adresse ====
Bei diesem Angriffstyp kann die eingetragene E-Mail-Adresse die eines **unbeteiligten Dritten** sein (Opfer, nicht Täter). Eine Meldung dieser Adresse an eine Spam-Datenbank wäre unfair und sachlich falsch. Deshalb: nur die IP wird gemeldet, nicht die Adresse selbst (siehe Skript, Schritt 3).
==== 7.5 Empfehlung: vor produktivem Einsatz zunächst nur per SELECT testen ====
Bevor ein solches Skript scharf geschaltet wird, empfiehlt sich ein reiner Prüf-Lauf (nur SELECT, keine UPDATE/INSERT-Anweisungen), um zu verifizieren, dass ausschließlich plausible Bot-Fälle erfasst würden — insbesondere nach jeder Anpassung des Erkennungskriteriums.
===== 8. Automatisierte IP-Sperrliste =====
==== 8.1 Konzept ====
Bekannte Bot-IPs (aus Honeypot-Treffern oder manueller Markierung) werden dauerhaft direkt auf Anwendungsebene gesperrt.
==== 8.2 Attribute ====
^ Name (Beispiel) ^ Typ ^
| „IP sperren" | Checkbox (manuelle Markierung) |
| „IP bereits gesperrt" | Checkbox (Duplikatschutz) |
==== 8.3 Skript: blocklist_update.php ====
{{page>gitea_code:phpList-Bot-Schutz:blocklist_update_php}}
=== 💬 Fragen & Feedback ===
Haben Sie Fehler gefunden, Verbesserungsvorschläge oder Fragen zu diesem Skript?
[[https://vw.falk.plus/JensFalk/phpList-Bot-Schutz/issues/new?labels=blocklist_update.php|Hier ein neues Gitea-Issue öffnen]]
==== 8.4 Lese-Check in config.php ====
Siehe [[#config_snippets|config_snippets.php]], Block 4.
**Wichtig:** Als PHP-Datei statt SQLite/separater Datenbank umgesetzt, da config.php bei jedem Seitenaufruf geladen wird – ein include ist deutlich performanter als eine zusätzliche Datenbankabfrage bei jedem Request.
==== 8.5 Grenze dieses Ansatzes ====
Bei Angriffen mit **ständig wechselnden IPs** gegen dieselbe Zieladresse (siehe Kapitel 9) bremst eine IP-Sperre einzelner Adressen nicht wirksam.
===== 9. Subscription-Bombing-Schutz (E-Mail-basiertes Rate-Limiting) =====
==== 9.1 Angriffsmuster ====
Manche Angreifer reichen dieselbe fremde Zieladresse wiederholt über das Formular ein – jedes Mal von einer anderen IP (rotierendes Proxy-/Botnetz). Zeitliche Abstände können von wenigen Minuten bis zu mehreren Stunden reichen.
**Wirkung:** Das System verschickt bei jeder Einreichung erneut eine Bestätigungsmail an die (oft nicht existierende oder unbeteiligte) Zieladresse – bekannt als „Subscription Bombing", Missbrauch fremder Formulare, um eine dritte Person mit E-Mails zu belästigen.
**Weder Honeypot noch IP-Sperre greifen zuverlässig:** Honeypot-Feld bleibt leer, IP wechselt bei jedem Versuch.
==== 9.2 Lösung: Rate-Limiting pro Zieladresse statt pro IP ====
Siehe [[#config_snippets|config_snippets.php]], Block 5.
**Empfohlene Parameter:** 24-Stunden-Zeitfenster, Blockierung ab dem 3. Versuch derselben Adresse. Ein kürzeres Fenster (z. B. 1 Stunde) reicht bei hartnäckigen Angreifern u. U. nicht aus, da Abstände zwischen Versuchen auch mal 1–2 Stunden betragen können.
**Abwägung:** In seltenen Fällen könnte ein echter Mensch betroffen sein (z. B. mehrfache legitime Neuanmeldung). Das lässt sich durch einen Hinweis auf dem Formular abfedern ("Bei Problemen bitte direkt Kontakt aufnehmen") – das Restrisiko einer versehentlichen Blockade wird gegen den Schutz vor Missbrauch abgewogen.
===== 10. Zentrale Konfigurationssammlung: config_snippets.php ===== {{anchor:config_snippets}}
Alle oben referenzierten Ergänzungen für die phpList ''config.php'' an einer Stelle gesammelt (keine eigenständig lauffähige Datei, sondern Referenz zum Übernehmen).
{{page>gitea_code:phpList-Bot-Schutz:config_snippets_php}}
=== 💬 Fragen & Feedback ===
Haben Sie Fehler gefunden, Verbesserungsvorschläge oder Fragen zu dieser Konfigurationssammlung?
[[https://vw.falk.plus/JensFalk/phpList-Bot-Schutz/issues/new?labels=config_snippets.php|Hier ein neues Gitea-Issue öffnen]]
===== 11. Cron-Jobs =====
^ # ^ Zweck ^ Empfohlene Frequenz ^
| 1 | Spam-Report an Stop Forum Spam | täglich (z. B. nachts) |
| 2 | IP-Sperrliste aktualisieren | alle 15–30 Minuten |
| 3 | Bounce-Verarbeitung | alle 1–2 Stunden |
| 4 | Blitzbestätigung erkennen & zurücksetzen | stündlich |
**Bei Hosting-Panels mit eigener Cron-Verwaltung (z. B. ISPConfig):**
* PHP-Skripte als „Full"/Shell-Cronjob mit vollem Pfad zum PHP-Interpreter
* Reine URL-Aufrufe (Remote-Call für Bounce-Verarbeitung) ggf. als eigener „URL"-Crontyp, der intern per wget arbeitet – dort keine Shell-Umleitung (>>, 2>&1) in die URL einbauen, das Logging übernimmt meist das Panel selbst
**Falls direkter SSH-Zugriff auf crontab fehlt** (Berechtigungseinschränkung): Cron-Jobs über die Hosting-Oberfläche einrichten.
===== 12. Bewusste Entscheidung: keine automatische Löschung unbestätigter/gesperrter Abonnenten =====
Unbestätigte, blacklistete oder als Bot erkannte Abonnenten sollten **nicht** automatisiert gelöscht werden, sondern dauerhaft in der Datenbank verbleiben – aus zwei Gründen:
- **Löschen würde die eigene Abwehr schwächen:** Die Report-, Sperrlisten- und Rate-Limiting-Skripte (Kapitel 5, 8, 9) prüfen jeweils gegen bestehende Zeilen (Duplikat-Attribute, Anzahl bisheriger Anmeldeversuche in der History-Tabelle). Würde ein Datensatz gelöscht, könnte derselbe Bot mit derselben Adresse erneut von vorne beginnen, da sein Zähler zurückgesetzt wäre.
- **Kein echter Speicherplatzvorteil:** Selbst mehrere tausend zusätzliche Zeilen sind für die Datenbankgröße vernachlässigbar; das Löschrisiko (versehentlich einen echten, nur verspätet reagierenden Interessenten zu treffen) steht in keinem Verhältnis zum Nutzen.
**Getrennt davon zu betrachten:** Eine echte Abmeldung eines Menschen (Sperrliste „nicht mehr kontaktieren") ist rechtlich zulässig und im Interesse der Person dauerhaft aufzubewahren – das ist ein anderer Fall als die hier beschriebene Bot-Bereinigung und wird nicht durch diese Überlegung berührt.
===== 13. Monitoring: eigene Listung auf Spam-Blacklists überwachen =====
==== 13.1 Hintergrund ====
Da automatisierte Skripte selbst Meldungen an Stop Forum Spam absetzen, besteht ein Restrisiko fehlerhafter Selbstmeldung (siehe Kapitel 7.2 — eine erste, noch fehlerhafte Skript-Version hätte beinahe eine eigene, feste IP-Adresse gemeldet). Zusätzlich können Dritte unabhängig davon eine Meldung auslösen.
==== 13.2 Empfohlenes Monitoring (z. B. mit Uptime Kuma oder ähnlichem Tool) ====
Drei Monitore, HTTP(s)-Keyword-Check mit „Invert Keyword" (Alarm, sobald das Keyword auftaucht), Intervall z. B. alle 12 Stunden:
^ Monitor ^ URL ^ Keyword ^
| Eigene feste IP | https://api.stopforumspam.com/api?ip=DEINE_IP&json | "appears":1 |
| Server-/Mailversand-IP | https://api.stopforumspam.com/api?ip=SERVER_IP&json | "appears":1 |
| Allgemeine Blacklist-Prüfung | https://mxtoolbox.com/SuperTool.aspx?action=blacklist%3aSERVER_IP&run=toolpage | Listing-Keyword prüfen, ggf. auf JavaScript-Rendering der Seite achten |
==== 13.3 Abwägung: Monitoring der Newsletter-Absenderadresse ====
Ein zusätzlicher Monitor für die tatsächliche Versandadresse des Newsletters (Schutz gegen Meldung durch Dritte, z. B. via Spam-Beschwerden von Empfängern) ist möglich, aber oft verzichtbar: Ein Blacklist-Monitoring der Server-IP deckt Domain-/Absenderreputation meist bereits mit ab, und das Risiko einer Selbstmeldung durch eigene Skripte besteht bei der Absenderadresse strukturell nicht, sofern diese – wie hier empfohlen – nie an SFS gemeldet wird. Abwägung zwischen zusätzlicher Absicherung und Wartungsaufwand treffen.
===== 14. Wichtige Grundsätze =====
* **Fairness gegenüber Stop Forum Spam:** Nur eindeutig identifizierte Bots melden (klares Kriterium wie ein Honeypot-Treffer), nicht pauschal jede unbekannte Adresse.
* **Niemals ungeprüft die eigene Server-IP als „Spammer" melden:** IP explizit mitschicken statt der automatischen Erkennung der anfragenden Verbindung zu vertrauen.
* **Bei Adress-Scan-/Bombing-Angriffen:** möglicherweise fremde/unbeteiligte E-Mail-Adressen nicht öffentlich als Spam melden – nur die Infrastruktur (IP) angehen.
* **Vor größeren Software-Updates:** eigenen Datenbank-Dump erstellen, auch wenn die Software selbst automatische Backups anlegt.
* **Eigene Skripte außerhalb des Web-Roots ablegen**, niemals im öffentlich erreichbaren Installationsverzeichnis.
* **Testdaten mit reservierten Dokumentations-Adressen** (z. B. IP nach RFC 5737 wie 203.0.113.1) statt echter eigener oder fremder Adressen anlegen.
----
//Allgemeine Anleitung, erstellt auf Basis einer erfolgreich umgesetzten phpList-Konfiguration. Skripte werden live aus Gitea (Repo: phpList-Bot-Schutz) synchronisiert.//