Markus Michalski FAQ

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.

English Version

Zurück zur Übersicht

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.