Datenschutzfreundlicher Spam-Schutz für Shopware 6 Kontakt-, Newsletter- und Registrierungsformulare mit ALTCHA Proof-of-Work, Honeypot-Feldern, Inhaltsvalidierung und IP-basiertem Rate Limiting.
Live Demo: Teste alle Plugins für OXID eShop und Shopware von Markus Michalski live — ohne Installation, ohne Risiko. demo.markus-michalski.net
ALTCHA ist eine datenschutzfreundliche CAPTCHA-Alternative, die auf einem Proof-of-Work-Mechanismus basiert. Im Gegensatz zu reCAPTCHA oder hCaptcha:
Der Client (Browser) muss eine kryptographische Aufgabe lösen: Eine Zahl N finden, sodass SHA-256(salt + N) dem Challenge-Hash entspricht. Das ist für Menschen unsichtbar (automatische Lösung im Hintergrund), für Bots aber aufwändig bei massenhaften Anfragen.
| Feature | Beschreibung |
|---|---|
| ALTCHA Proof-of-Work | DSGVO-konforme CAPTCHA-Alternative - keine Cookies, kein Tracking, keine externen Dienste |
| Honeypot-Felder | Unsichtbare Falle, die Bots erkennt, ohne echte Benutzer zu beeinträchtigen |
| Inhaltsvalidierung | Erkennung von Gibberish-Text, Tastatursequenzen und Wegwerf-E-Mail-Adressen |
| IP Rate Limiting | Datenbankgestütztes Sliding-Window Rate Limiting mit konfigurierbaren Schwellwerten |
| Replay-Schutz | Benutzte Challenges werden verfolgt und abgelehnt |
| Challenge DoS-Schutz | Separates Rate Limiting für den Challenge-Endpunkt |
| Multi-Sales-Channel | Konfigurierbar pro Verkaufskanal |
| Mehrsprachiges Widget | Deutsche und englische Widget-Texte über Shopware-Snippets |
| DSGVO-konform | IP-Adressen als SHA-256-Hashes, automatische Datenbereinigung |
Das Plugin schützt die folgenden Shopware-Formulare:
ContactFormEvent SubscriberNewsletterRegisterEvent SubscriberRegisterRoute DecoratorJedes Formular kann individuell aktiviert/deaktiviert werden.
Die Installation über den Shopware Store wird empfohlen. Nach dem Kauf kann das Plugin direkt aus dem Backend installiert werden.
Füge das private Composer-Repository zur composer.json deines Shops hinzu:
{
"repositories": [
{
"type": "composer",
"url": "https://packeton.markus-michalski.net"
}
]
}
Die Repository-Zugangsdaten werden nach dem Kauf bereitgestellt. Die Verwaltung erfolgt über Packeton.
composer require mmd/sw67-altcha-integration
Bei der Abfrage der Authentifizierung die mit der Lizenz bereitgestellten Zugangsdaten eingeben.
bin/console plugin:refresh
bin/console plugin:install --activate MmdAltchaIntegration
bin/console cache:clear
Update auf die neueste Version:
composer update mmd/sw67-altcha-integration
bin/console cache:clear
Die gesamte Konfiguration erfolgt über Einstellungen > System > Plugins > ALTCHA Spam-Schutz im Shopware Admin. Alle Einstellungen sind pro Sales Channel konfigurierbar.
| Einstellung | Standard | Beschreibung |
|---|---|---|
| Spam-Schutz aktivieren | true |
Master Switch - deaktiviert alle Schutzfunktionen |
| Newsletter-Formular schützen | true |
Spam-Schutz für Newsletter-Anmeldeformulare |
| Registrierungsformular schützen | true |
Spam-Schutz für die Kundenregistrierung |
| Einstellung | Standard | Beschreibung |
|---|---|---|
| ALTCHA-Widget aktivieren | true |
Zeigt "Ich bin kein Roboter"-Checkbox mit automatischer Verifizierung |
| Challenge-Schwierigkeit (Max Number) | 50000 |
Höherer Wert = schwierigere Challenge. Empfohlen: 50.000 |
| Challenge-Gültigkeit (Sekunden) | 300 |
Wie lange eine Challenge gültig ist (Standard: 5 Minuten) |
| Einstellung | Standard | Beschreibung |
|---|---|---|
| Honeypot-Feld aktivieren | true |
Fügt ein unsichtbares Feld (fax_number) hinzu, das Bots fängt |
Das Honeypot-Feld wird mit position:absolute; left:-9999px versteckt (nicht display:none, was Bots überspringen). Es verwendet tabindex="-1" und autocomplete="off", um echte Benutzer nicht zu beeinträchtigen.
| Einstellung | Standard | Beschreibung |
|---|---|---|
| Inhaltsvalidierung aktivieren | true |
Prüft Formularfelder auf Gibberish und Wegwerf-E-Mails |
Die Inhaltsvalidierung prüft:
firstName, lastName, billingAddress.street, billingAddress.city:
asdf, ghjk, qwert, etc.)Die Validierung erfordert mindestens 2 verdächtige Felder, um eine Einreichung zu blockieren. Das minimiert Fehlalarme bei ungewöhnlichen, aber echten Namen.
| Einstellung | Standard | Beschreibung |
|---|---|---|
| IP-basiertes Rate Limiting | true |
Begrenzt Formular-Übermittlungen pro IP-Adresse |
| Max. Anfragen | 3 |
Maximale Anzahl pro Zeitfenster |
| Zeitfenster (Minuten) | 60 |
Sliding Window für Rate Limiting (Standard: 1 Stunde) |
Das Rate Limiting verwendet ein echtes Sliding Window (kein Fixed Window), wodurch Edge Cases an Fenstergrenzen vermieden werden. IP-Adressen werden als HMAC-SHA256-Hashes gespeichert (DSGVO Art. 5(1)(f) konform).
| Einstellung | Standard | Beschreibung |
|---|---|---|
| Challenge Rate Limiting | true |
Begrenzt Challenge-Anfragen pro IP |
| Max. Challenges pro IP | 30 |
Maximale Challenge-Anfragen im Zeitfenster |
| Zeitfenster (Minuten) | 60 |
Zeitfenster für Challenge Rate Limiting |
Dieses separate Rate Limiting schützt den /altcha/challenge-Endpunkt vor DoS-Angriffen. Die Limits sind bewusst höher als beim Formular-Rate-Limiting (30 vs. 3), da ein Benutzer mehrere Formulare auf einer Seite besuchen kann.
Alle blockierten Spam-Versuche werden im Shopware-Log protokolliert:
[INFO] Spam attempt blocked {"reason":"honeypot_triggered","ip_hash":"a1b2c3...","sales_channel_id":"..."}
[INFO] Newsletter spam attempt blocked {"reason":"rate_limited","ip_hash":"...","sales_channel_id":"..."}
[INFO] Registration spam attempt blocked {"reason":"content_validation_failed","ip_hash":"...","sales_channel_id":"..."}
Die geloggten Gründe (reason) sind:
honeypot_triggered - Honeypot-Feld wurde ausgefülltcontent_validation_failed - Gibberish oder Wegwerf-E-Mail erkanntaltcha_failed - ALTCHA-Challenge-Lösung ungültigaltcha_missing - Kein ALTCHA-Payload im Requestrate_limited - IP-Rate-Limit überschrittenDie Validierungsreihenfolge ist auf Performance optimiert - günstigste Prüfungen zuerst:
Formular-Einreichung
|
v
1. Honeypot (In-Memory, günstigste Prüfung)
|
v
2. Inhaltsvalidierung (In-Memory, String-Analyse)
|
v
3. ALTCHA (Krypto-Verifikation + DB für Replay-Schutz)
|
v
4. Rate Limit (Atomisches Check+Log, DB I/O)
|
v
Formular wird verarbeitet
Jede Schicht kann unabhängig aktiviert/deaktiviert werden. Wenn eine Prüfung fehlschlägt, werden die nachfolgenden Prüfungen nicht ausgeführt (Fail-Fast).
Das Rate Limiting wird zuletzt ausgeführt und atomar (Check + Log in einer Operation), damit nur gültige Anfragen gezählt werden und Race Conditions verhindert werden.
Die Registrierung wird über einen Route Decorator geschützt (RegisterRouteDecorator), nicht über einen Event Subscriber. Das hat einen wichtigen Vorteil: Der Spam-Check findet vor der Persistierung statt - es werden keine Fake-Konten in der Datenbank angelegt.
Das ALTCHA-Widget verwendet Shopware-Snippets für die Lokalisierung:
storefront.de-DE.json)| Snippet-Key | Text |
|---|---|
mmdAltcha.widget.label |
Ich bin kein Roboter |
mmdAltcha.widget.verifying |
Wird überprüft... |
mmdAltcha.widget.verified |
Überprüft |
mmdAltcha.widget.error |
Überprüfung fehlgeschlagen |
mmdAltcha.widget.expired |
Abgelaufen - Seite neu laden |
mmdAltcha.error.spamDetected |
Ihre Nachricht konnte nicht gesendet werden. Bitte versuchen Sie es später erneut. |
mmdAltcha.error.rateLimitExceeded |
Zu viele Anfragen. Bitte versuchen Sie es später erneut. |
mmdAltcha.error.verificationFailed |
Sicherheitsüberprüfung fehlgeschlagen. Bitte versuchen Sie es erneut. |
mmdAltcha.error.registrationBlocked |
Ihre Registrierung konnte nicht abgeschlossen werden. Bitte versuchen Sie es später erneut. |
storefront.en-GB.json)| Snippet-Key | Text |
|---|---|
mmdAltcha.widget.label |
I'm not a robot |
mmdAltcha.widget.verifying |
Verifying... |
mmdAltcha.widget.verified |
Verified |
mmdAltcha.widget.error |
Verification failed |
mmdAltcha.widget.expired |
Expired - reload page |
mmdAltcha.error.spamDetected |
Your message could not be sent. Please try again later. |
mmdAltcha.error.rateLimitExceeded |
Too many requests. Please try again later. |
mmdAltcha.error.verificationFailed |
Security verification failed. Please try again. |
mmdAltcha.error.registrationBlocked |
Your registration could not be completed. Please try again later. |
Die Snippets können im Shopware Admin unter Einstellungen > Shop > Snippets angepasst werden.
Das Plugin erstellt zwei Tabellen:
mmd_altcha_rate_limit_logSpeichert individuelle Request-Timestamps für Sliding-Window Rate Limiting.
| Spalte | Typ | Beschreibung |
|---|---|---|
id |
BINARY(16) |
Primärschlüssel |
ip_hash |
CHAR(64) |
SHA-256 HMAC-Hash der IP-Adresse (DSGVO-konform) |
action |
VARCHAR(50) |
Aktions-Identifier: contact_form, newsletter, registration, challenge |
sales_channel_id |
BINARY(16) |
Optional: Verkaufskanal |
requested_at |
DATETIME(3) |
Zeitstempel der Anfrage |
Indizes: idx_rate_check (ip_hash, action, requested_at), idx_rate_check_channel (mit sales_channel_id), idx_cleanup (requested_at).
mmd_altcha_used_challengeVerhindert Replay-Angriffe durch Tracking benutzer Challenges.
| Spalte | Typ | Beschreibung |
|---|---|---|
challenge_hash |
CHAR(64) |
SHA-256 Hash der Challenge (Primärschlüssel) |
expires_at |
DATETIME(3) |
Ablaufzeitpunkt der Challenge |
created_at |
DATETIME(3) |
Erstellungszeitpunkt |
Verwendet INSERT IGNORE für atomisches Claiming - nur der erste Schreiber gewinnt (verhindert TOCTOU Race Conditions).
Der Task mmd_altcha.rate_limit_cleanup läuft alle 6 Stunden und bereinigt:
Dies hält die Tabellen klein und performant und stellt die DSGVO-konforme Datenlöschung sicher.
/altcha/challengeGeneriert eine neue ALTCHA-Challenge für das Widget.
Scope: Storefront (AJAX)
Response (200 OK):
{
"algorithm": "SHA-256",
"challenge": "a1b2c3d4e5f6...",
"salt": "random_hex.timestamp.expiry",
"signature": "hmac_sha256_signature",
"maxNumber": 50000
}
Fehler-Responses:
| Status | Beschreibung |
|---|---|
503 Service Unavailable |
ALTCHA ist deaktiviert |
429 Too Many Requests |
Challenge Rate Limit überschritten (mit Retry-After Header) |
Das Plugin stellt folgende Twig-Funktionen für die Theme-Integration bereit:
| Funktion | Rückgabe | Beschreibung |
|---|---|---|
altcha_enabled(salesChannelId) |
bool |
Prüft ob ALTCHA aktiviert ist (berücksichtigt Master Switch) |
honeypot_enabled(salesChannelId) |
bool |
Prüft ob Honeypot aktiviert ist |
honeypot_field(salesChannelId) |
string (HTML) |
Rendert das Honeypot-Feld (verstecktes Input-Element) |
newsletter_protection_enabled(salesChannelId) |
bool |
Prüft ob Newsletter-Schutz aktiviert ist |
registration_protection_enabled(salesChannelId) |
bool |
Prüft ob Registrierungs-Schutz aktiviert ist |
Beispiel in Twig:
{% if altcha_enabled(context.salesChannel.id) %}
<altcha-widget
challengeurl="/altcha/challenge"
strings='{"label":"{{ "mmdAltcha.widget.label"|trans }}"}'
></altcha-widget>
{% endif %}
{{ honeypot_field(context.salesChannel.id) }}
Das Plugin wurde mit Datenschutz als oberstem Prinzip entwickelt:
| Aspekt | Umsetzung |
|---|---|
| Keine externen Dienste | Alle Prüfungen laufen lokal auf dem eigenen Server |
| Keine Cookies | ALTCHA benötigt keine Cookies |
| Kein Tracking | Kein Fingerprinting, keine Analyse des Nutzerverhaltens |
| IP-Hashing | IP-Adressen werden als HMAC-SHA256-Hash gespeichert (nicht rekonstruierbar) |
| Automatische Löschung | Rate-Limit-Daten werden nach 24 Stunden bereinigt |
| Datenminimierung | Es werden nur die minimal notwendigen Daten gespeichert |
| Kein Datentransfer | Keine Daten verlassen den Server |
Ein Eintrag in der Datenschutzerklärung ist empfehlenswert, da IP-Hashes als personenbezogene Daten gelten können. Eine Vorlage wird in Zukunft bereitgestellt.
| Feature | ALTCHA Plugin | reCAPTCHA | hCaptcha | Honeypot-only |
|---|---|---|---|---|
| DSGVO-konform | Ja | Nein | Teilweise | Ja |
| Keine Cookies | Ja | Nein | Nein | Ja |
| Kein Tracking | Ja | Nein | Nein | Ja |
| Externe Dienste | Nein | Ja (Google) | Ja | Nein |
| Bot-Erkennung | Sehr gut | Sehr gut | Sehr gut | Begrenzt |
| Rate Limiting | Integriert | Nein | Nein | Nein |
| Inhaltsvalidierung | Integriert | Nein | Nein | Nein |
| Replay-Schutz | Ja | Ja | Ja | Nein |
| Barrierefreiheit | Gut | Problematisch | Problematisch | Sehr gut |
| Benutzerfreundlichkeit | Automatisch | Klick/Puzzle | Puzzle | Unsichtbar |
true stehtbin/console cache:clearbin/console messenger:consumebin/console scheduled-task:run mmd_altcha.rate_limit_cleanupMmdAltchaIntegration/
├── Core/
│ ├── Checkout/Customer/SalesChannel/
│ │ └── RegisterRouteDecorator # Decorator-Pattern für Registrierung
│ └── Content/RateLimitLog/
│ ├── RateLimitLogDefinition # DAL Entity Definition
│ ├── RateLimitLogEntity # Entity
│ └── RateLimitLogCollection # Collection
├── Exception/
│ └── SpamDetectedException # Exception bei erkanntem Spam
├── Migration/
│ ├── ...CreateRateLimitLogTable # Rate-Limit Tabelle
│ └── ...CreateUsedChallengeTable # Replay-Schutz Tabelle
├── ScheduledTask/
│ ├── RateLimitCleanupTask # Task-Definition (6h Intervall)
│ └── RateLimitCleanupTaskHandler # Cleanup-Logik
├── Service/
│ ├── AltchaChallengeService # Challenge-Generierung + Verifikation
│ ├── ChallengeRateLimitService # DoS-Schutz für Challenge-Endpunkt
│ ├── ContentValidationService # Gibberish + Wegwerf-E-Mail Erkennung
│ ├── HoneypotService # Honeypot-Feld Logik
│ ├── RateLimitService # Sliding-Window Rate Limiting (DAL)
│ └── SpamProtectionFacade # Orchestriert alle Schutzschichten
├── Storefront/Controller/
│ └── AltchaChallengeController # GET /altcha/challenge Endpunkt
├── Struct/
│ ├── AltchaChallenge # Challenge VO
│ ├── AltchaSolution # Solution VO (aus Base64)
│ ├── ChallengeRateLimitResult # Rate-Limit Ergebnis
│ ├── SpamCheckRequest # Request VO für Validierung
│ └── SpamCheckResult # Ergebnis mit Reason
├── Subscriber/
│ ├── ContactFormSubscriber # ContactFormEvent Handler
│ └── NewsletterSubscriber # NewsletterRegisterEvent Handler
└── Twig/
└── AltchaExtension # Twig-Funktionen
APP_SECRET Umgebungsvariable abgeleitetINSERT IGNORE verhindert Race Conditionshash_equals() gegen Timing-AngriffeKommerzielle Lizenz - Einzelinstallation-Lizenz inklusive aller Sales Channels und Entwicklungs-/Staging-Umgebungen.
Markus Michalski - support@markus-michalski.net