Markus Michalski FAQ

ALTCHA: Technische Details

ALTCHA Spam-Schutz für Shopware 6.7 - Prüfreihenfolge als Diagramm, Registrierungsschutz, Gibberish-Score und veraltete Inhaltsvalidierung, Datenbank-Tabellen, Logging, Sicherheitskonzept, Plugin-Struktur

English Version

Zurück zur Übersicht

ALTCHA Spam-Schutz: Technische Details

Diese Dokumentation bezieht sich immer auf die aktuellste veröffentlichte Version.

Live Demo: Teste dieses und weitere Plugins live — ohne Installation, ohne Risiko: demo.markus-michalski.net

Prüfreihenfolge

Alle drei geschützten Formulare laufen durch dieselbe SpamProtectionFacade. Die Reihenfolge ist nach Kosten sortiert: günstige Prüfungen im Speicher zuerst, die kryptographische ALTCHA-Prüfung danach, das Rate Limit mit Datenbankzugriff zuletzt.

graph TD
    A["Absendung"] --> M{"Hauptschalter an?"}
    M -->|"Nein"| OK["Formular wird verarbeitet"]
    M -->|"Ja"| B{"Honeypot-Feld ausgefüllt?"}
    B -->|"Ja"| R1["Abgelehnt: honeypot_triggered"]
    B -->|"Nein"| C{"Schneller als Mindest-Ausfüllzeit?"}
    C -->|"Ja"| R2["Abgelehnt: too_fast"]
    C -->|"Nein"| D{"Wegwerf-Mail aktiv und Treffer?"}
    D -->|"Ja"| R3["Abgelehnt: disposable_email"]
    D -->|"Nein"| E{"Gibberish aktiv und Score zu hoch?"}
    E -->|"Ja"| R4["Abgelehnt: gibberish"]
    E -->|"Nein"| F{"Inhaltsvalidierung aktiv, nicht ersetzt, 2+ Treffer?"}
    F -->|"Ja"| R5["Abgelehnt: content_validation_failed"]
    F -->|"Nein"| G{"ALTCHA gültig?"}
    G -->|"Nein"| R6["Abgelehnt: altcha_failed oder altcha_missing"]
    G -->|"Ja"| H{"Rate Limit ok?"}
    H -->|"Nein"| R7["Abgelehnt: rate_limited"]
    H -->|"Ja"| I["Formular wird verarbeitet"]

Eine abgeschaltete Schicht wird übersprungen. Die Inhaltsvalidierung wird zusätzlich übersprungen, sobald Gibberish-Erkennung oder Wegwerf-E-Mail-Prüfung aktiv ist. Das Rate Limit zählt nur Absendungen, die alle Schichten davor bestanden haben.

Registrierungsschutz

Die Registrierung wird über einen Route Decorator geschützt, nicht über ein Event. Die Prüfung läuft deshalb vor dem Anlegen des Kontos.

sequenceDiagram
    participant B as Browser
    participant D as RegisterRouteDecorator
    participant F as SpamProtectionFacade
    participant R as RegisterRoute
    B->>D: Registrierung absenden
    D->>D: Schutz aktiv?
    D->>F: validate
    F-->>D: Ergebnis
    alt Spam erkannt
        D-->>B: Abgelehnt
    else kein Spam
        D->>R: register
        R-->>B: Konto angelegt
    end

Kontaktformular und Newsletter laufen über Event-Subscriber auf contact_form.send und NewsletterRegisterEvent (Priorität 500), mit derselben Facade.

Gibberish-Score

Die Gibberish-Erkennung bewertet jedes Textfeld und nimmt den schlechtesten Wert. Der Score ist der Anteil der Buchstaben in auffälligen Wörtern, gewichtet nach der Menge an Beweisen:

Signal Beschreibung
Buchstabenfolgen Die Trigramme eines Wortes sind in keiner Sprache der mitgelieferten Tabelle plausibel (Deutsch, Englisch, Französisch, Spanisch, Italienisch, Niederländisch)
Tastaturläufe Fast alle Buchstaben eines Wortes liegen als Nachbarn in einer Tastaturzeile (asdfgh, qwertz)
Wiederholungen Ein Wort besteht aus einer kurzen, wiederholten Einheit (asdfasdf)

Übersprungen werden URLs, E-Mail-Adressen, Code, Wörter mit Ziffern oder Sonderzeichen und Wörter in nicht lateinischer Schrift. Groß geschriebene Wörter zählen weniger (Namen), und kurze Wörter lösen allein nur selten eine Ablehnung aus. Ein langes, klein geschriebenes Wort aus unmöglichen Buchstabenfolgen kann dagegen schon allein einen hohen Score erreichen. Die Tabelle liegt unter src/Resources/data/gibberish-trigrams.php. Fehlt sie oder ist sie beschädigt, bewertet die Prüfung nichts und protokolliert das einmalig als Warnung.

Die veraltete Inhaltsvalidierung arbeitet dagegen mit festen Regeln (Konsonantenanteil, Tastatursequenzen, Wiederholungen, Konsonantencluster, Buchstabenpaare) auf Vorname, Nachname, Straße und Ort sowie der E-Mail-Domain und blockiert ab zwei auffälligen Feldern.

Datenbank-Tabellen

mmd_altcha_rate_limit_log

Speichert einzelne Zeitstempel für ein gleitendes Zeitfenster.

Spalte Typ Beschreibung
id BINARY(16) Primärschlüssel
ip_hash CHAR(64) HMAC-SHA256-Hash der IP-Adresse
action VARCHAR(50) contact_form, newsletter, registration, challenge
sales_channel_id BINARY(16) optional, Fremdschlüssel auf sales_channel
requested_at DATETIME(3) Zeitpunkt der Anfrage

Indizes: idx_rate_check (ip_hash, action, requested_at), idx_rate_check_channel (zusätzlich sales_channel_id) und idx_cleanup (requested_at).

mmd_altcha_used_challenge

Verhindert Replay-Angriffe.

Spalte Typ Beschreibung
challenge_hash CHAR(64) Primärschlüssel, Nonce der Challenge
expires_at DATETIME(3) Ablaufzeitpunkt
created_at DATETIME(3) Erstellungszeitpunkt

INSERT IGNORE beansprucht eine Challenge atomar: Nur der erste Schreiber gewinnt.

Die Wegwerf-Domain-Liste und die Trigramm-Tabelle liegen als Dateien im Plugin beziehungsweise unter var/mmd_altcha/, nicht in der Datenbank.

Logging und Ablehnungsgründe

Blockierte Versuche stehen im Shopware-Log, nie mit dem eingereichten Text:

[INFO] Spam attempt blocked {"reason":"honeypot_triggered","ip_hash":"...","sales_channel_id":"..."}
[INFO] Registration spam attempt blocked {"reason":"disposable_email","ip_hash":"...","sales_channel_id":"..."}
reason Bedeutung
honeypot_triggered Honeypot-Feld war ausgefüllt
too_fast Absendung schneller als die Mindest-Ausfüllzeit
disposable_email Wegwerf-E-Mail-Adresse erkannt
gibberish Gibberish-Score erreicht die Ablehnungsschwelle
content_validation_failed Zwei oder mehr Felder bei der veralteten Inhaltsvalidierung auffällig
altcha_missing Kein ALTCHA-Payload im Request
altcha_failed ALTCHA-Lösung ungültig, abgelaufen oder schon benutzt
rate_limited IP-Rate-Limit überschritten

Sicherheitskonzept

graph TD
    A["APP_SECRET"] -->|"signiert"| B["ALTCHA-Challenge"]
    A -->|"signiert"| C["Formular-Token"]
    A -->|"hasht"| D["IP-Adressen"]
    B -->|"gelöst und geprüft"| E["Nonce wird beansprucht"]
    E -->|"INSERT IGNORE"| F["mmd_altcha_used_challenge"]
    D --> G["mmd_altcha_rate_limit_log"]
    H["Task rate_limit_cleanup"] -->|"alle 6 Stunden"| F
    H --> G

Plugin-Struktur

MmdAltchaIntegration/
├── Altcha/                              # gebündelte altcha-org/altcha-Bibliothek (MIT)
├── Command/UpdateDisposableDomainsCommand   # mmd:altcha:update-disposable-domains
├── Controller/Api/BlockedDomainsController  # GET /api/_action/mmd-altcha/blocked-domains
├── Core/
│   ├── Checkout/Customer/SalesChannel/RegisterRouteDecorator
│   └── Content/RateLimitLog/            # Entity-Definition, Entity, Collection
├── DisposableEmail/                     # Checker, Listenleser, Updater, Katalog, Parser
├── Exception/                           # SpamDetectedException, SpamRejection
├── Gibberish/                           # Guard, Scorer, Score, TrigramModel
├── Migration/                           # Rate-Limit- und Used-Challenge-Tabelle
├── ScheduledTask/                       # rate_limit_cleanup, disposable_domains_refresh
├── Service/                             # Challenge, Honeypot, FormToken, RateLimit, Facade
├── Storefront/Controller/AltchaChallengeController
├── Subscriber/                          # ContactFormSubscriber, NewsletterSubscriber
└── Twig/AltchaExtension                 # Twig-Funktionen für Templates