ALTCHA Spam-Schutz: Technische Details
Technik des ALTCHA Spam-Schutz Moduls für OXID 7: Prüfkette, erweiterte Core-Klassen, Modulstruktur, Services und die beiden Datenbank-Tabellen mit Spalten, Aktionen und Aufbewahrung.
ALTCHA Spam-Schutz: Technische Details
Live Demo: Teste dieses und weitere Plugins live — ohne Installation, ohne Risiko: demo.markus-michalski.net
Prüfkette
Die Klasse SpamProtectionFacade führt die Prüfungen in dieser Reihenfolge aus. Die erste fehlgeschlagene Prüfung beendet die Kette:
graph TD
A["Absendung"] --> B{"Spam-Schutz aktiviert?"}
B -->|"Nein"| Z["Akzeptiert"]
B -->|"Ja"| C{"Honeypot aktiv und ausgefüllt?"}
C -->|"Ja"| X1["Abgelehnt: honeypot_triggered"]
C -->|"Nein"| D{"Inhaltsvalidierung: 2 oder mehr auffällige Felder?"}
D -->|"Ja"| X2["Abgelehnt: content_validation_failed"]
D -->|"Nein"| E{"ALTCHA aktiv und Lösung ungültig?"}
E -->|"Ja"| X3["Abgelehnt: altcha_missing, altcha_invalid, altcha_expired, altcha_replay"]
E -->|"Nein"| F{"Rate Limit aktiv und überschritten?"}
F -->|"Ja"| X4["Abgelehnt: rate_limit_exceeded"]
F -->|"Nein"| Z
Eine Absendung muss alle aktivierten Prüfungen bestehen. Die Inhaltsvalidierung wird nur ausgeführt, wenn das Formular auswertbare Felder liefert. Die ALTCHA-Lösung wird vor dem Rate Limit geprüft und dabei als verbraucht markiert. Das Rate Limit zählt nur Absendungen, die die vorherigen Prüfungen bestanden haben.
Erweiterte OXID-Klassen
Die Erweiterungen sind in metadata.php unter extend eingetragen:
| OXID-Klasse | Modul-Klasse | Zweck |
|---|---|---|
OxidEsales\Eshop\Core\ViewConfig |
Mmd\AltchaIntegration\Extension\Core\ViewConfig |
Hilfsmethoden für Templates |
OxidEsales\Eshop\Application\Controller\ContactController |
Mmd\AltchaIntegration\Extension\Controller\ContactController |
Prüfung vor send() |
OxidEsales\Eshop\Application\Controller\NewsletterController |
Mmd\AltchaIntegration\Extension\Controller\NewsletterController |
Prüfung vor send() |
OxidEsales\Eshop\Application\Component\UserComponent |
Mmd\AltchaIntegration\Extension\Component\UserComponent |
Prüfung vor createUser(), nur bei cl=register |
Zusätzlich registriert das Modul den Controller mmd_altcha_challenge (Mmd\AltchaIntegration\Controller\AltchaChallengeController) und das Template @mmd_altcha/widget/altcha_widget.
Modulstruktur
oxid7-altcha-integration/
├── metadata.php # Modul-ID mmd_altcha, Einstellungen, Events
├── services.yaml # Services (autowire, öffentlich)
├── src/
│ ├── Controller/ # AltchaChallengeController
│ ├── Core/ # ModuleEvents (onActivate, onDeactivate)
│ ├── Exception/ # drei Exception-Klassen, im Modul nirgends verwendet
│ ├── Extension/ # Erweiterungen der OXID-Klassen
│ ├── Repository/ # RateLimitRepository, UsedChallengeRepository
│ ├── Service/ # Prüfservices und Einstellungen
│ └── Struct/ # SpamCheckRequest, SpamCheckResult, AltchaChallenge, AltchaSolution
├── translations/ # Frontend-Übersetzungen (de, en)
└── views/
├── admin_twig/ # Admin-Übersetzungen (de, en)
└── twig/ # Widget und Apex-Overrides
Services
| Service | Aufgabe |
|---|---|
SpamProtectionFacade |
Führt die Prüfkette aus |
HoneypotService |
Prüft, ob das Honeypot-Feld nicht leer ist |
ContentValidationService |
Gibberish- und Wegwerf-Domain-Prüfung |
AltchaChallengeService |
Erzeugt Challenges und prüft Lösungen |
RateLimitService |
Rate Limit für Formulare |
ChallengeRateLimitService |
Rate Limit für den Challenge-Endpunkt |
ModuleSettingsService |
Liest die Einstellungen, mit Standardwerten als Rückfall |
RateLimitRepository, UsedChallengeRepository |
Datenbankzugriff |
Kann eine Einstellung nicht gelesen werden, verwendet ModuleSettingsService den Standardwert.
Datenbank-Tabellen
Die Aktivierung legt beide Tabellen mit CREATE TABLE IF NOT EXISTS an (InnoDB, utf8mb4).
mmd_altcha_rate_limit_log
Eine Zeile pro Anfrage:
| Spalte | Typ | Inhalt |
|---|---|---|
id |
BIGINT UNSIGNED | Primärschlüssel, aus Mikrosekunden-Zeitstempel und Zufallszahl gebildet |
ip_hash |
CHAR(64) | HMAC-SHA256 aus ip: plus Client-Adresse mit dem HMAC-Geheimschlüssel |
action |
VARCHAR(50) | contact_form, newsletter_subscribe, registration oder challenge |
requested_at |
DATETIME | Zeitpunkt der Anfrage |
created_at |
DATETIME | Zeitpunkt des Eintrags |
Indizes: idx_rate_check (ip_hash, action, requested_at) und idx_cleanup (requested_at). Das Limit zählt die Zeilen mit passendem Hash und passender Aktion, deren requested_at im Zeitfenster liegt. Erst wenn die Zahl kleiner als das Maximum ist, wird eine neue Zeile geschrieben.
mmd_altcha_used_challenges
| Spalte | Typ | Inhalt |
|---|---|---|
id |
BIGINT UNSIGNED | Primärschlüssel |
challenge_hash |
CHAR(64) | SHA-256 der verwendeten Challenge, eindeutig |
expires_at |
DATETIME | Zeitpunkt der Verwendung plus "Challenge-Gültigkeit (Sekunden)" |
created_at |
DATETIME | Zeitpunkt des Eintrags |
Der eindeutige Index auf challenge_hash macht das Eintragen atomar: Wer dieselbe Challenge ein zweites Mal einreicht, fügt keine Zeile hinzu und wird als Wiederverwendung abgelehnt. Index idx_cleanup liegt auf expires_at.
Aufbewahrung
Das Modul löscht keine Zeilen. Die Methoden RateLimitService::cleanup(), RateLimitRepository::deleteOlderThan() und UsedChallengeRepository::deleteExpired() sind vorhanden, werden aber nirgends aufgerufen, es gibt weder einen Cronjob noch einen Hook. Beide Tabellen wachsen unbegrenzt, solange du sie nicht selbst aufräumst (Anleitung auf der Seite Datenschutz).
Modul-Events
| Event | Verhalten |
|---|---|
onActivate |
Legt die beiden Tabellen an |
onDeactivate |
Tut nichts |
Übersetzungen
Die Frontend-Texte (Widget-Beschriftungen und Fehlermeldungen, Schlüssel mit Präfix MMD_ALTCHA_) stehen in translations/de/mmd_altcha_lang.php und translations/en/mmd_altcha_lang.php. Die Admin-Bezeichnungen der Einstellungen stehen in views/admin_twig/de/mmd_altcha_lang.php und views/admin_twig/en/mmd_altcha_lang.php.