Shopware CaptchaException: „The provided value for captcha HoneypotCaptcha is not valid“ – Bots oder echte Kunden?
Das Log eines ganz normalen Shopware-Shops, ein Dienstagmorgen: Hunderte CaptchaExceptions über Nacht, alle 403, alle auf Registrierung, Kontaktformular oder Newsletter. Der erste Gedanke ist meistens „das Formular ist kaputt“. In den allermeisten Fällen stimmt das nicht. Das Captcha macht genau seinen Job. Es protokolliert ihn nur sehr laut.
Das gehört zu den häufigsten Storefront-Fehlern überhaupt. Meist trifft es den Honeypot, seltener Google reCAPTCHA v3 oder v2.
Die Fehlermeldung
Uncaught PHP Exception Shopware\Storefront\Framework\Captcha\CaptchaException:
"The provided value for captcha "Shopware\Storefront\Framework\Captcha\HoneypotCaptcha" is not valid."
at CaptchaException.php line 16
{"exception":"[object] (Shopware\\Storefront\\Framework\\Captcha\\CaptchaException(code: 0):
The provided value for captcha \"Shopware\\Storefront\\Framework\\Captcha\\HoneypotCaptcha\" is not valid.
at /var/www/shop/vendor/shopware/storefront/Framework/Captcha/CaptchaException.php:16)"}
Dieselbe Meldung gibt es mit GoogleReCaptchaV3 und GoogleReCaptchaV2 statt HoneypotCaptcha. Unter Shopware 6.5 heißt die Klasse noch CaptchaInvalidException, der Fehlertext ist derselbe. Der Error-Code ist in allen Versionen FRAMEWORK__INVALID_CAPTCHA_VALUE.
Was dahintersteckt
Shopware prüft Captchas in CaptchaRouteListener, und zwar nur auf Routen mit dem Attribut _captcha. In 6.6 sind das drei POST-Routen:
/account/register(frontend.account.register.save)/form/contact(frontend.form.contact.send)/form/newsletter(frontend.form.newsletter.register.handle)
Welche Captchas aktiv sind, steht pro Verkaufskanal in core.basicInformation.activeCaptchasV2. Schlägt eins fehl und meldet shouldBreak() true, wirft der Listener CaptchaException::invalid() mit Status 403. Bis 6.7.13 ist das bei allen mitgelieferten Captchas der Fall, der Standardwert in AbstractCaptcha ist true. Ein gescheitertes Captcha wird also nicht als Formularfehler angezeigt, sondern landet als ungefangene Exception im Log.
Der Honeypot ist ein unsichtbares Textfeld namens shopware_surname_confirm. Ein Mensch sieht es nicht und lässt es leer. Ein Bot, der stumpf alle Felder ausfüllt, schreibt etwas hinein und fliegt raus. Bei reCAPTCHA v3 scheitert die Prüfung, wenn das Token _grecaptcha_v3 fehlt oder der Score unter dem Schwellwert liegt (Standard 0.5). Ein Skript, das direkt auf /account/register postet, ohne die Seite im Browser zu laden, hat nie ein Token.
Für Bots ist das also das gewünschte Verhalten. Ärgerlich wird es erst, wenn echte Kunden betroffen sind.
Wann echte Kunden den Honeypot auslösen
Das ist dokumentiert, und zwar mit Firefox. Im Shopware-Forum beschreiben mehrere Shopbetreiber, dass Kunden sich unter 6.4.20 und 6.5.8 nicht registrieren konnten, sobald Firefox die gespeicherte Adresse automatisch eingetragen hat. Firefox ignoriert autocomplete="off" und füllt dann auch das versteckte Feld.
Behoben ist das seit Shopware 6.6.10.0 (NEXT-39199, „Change autocomplete to new-password in Honeypot“). Seitdem steht im Template autocomplete="new-password", mit dem Kommentar, dass Firefox off ignoriert. Wer älter ist oder ein Theme hat, das den Block component_captcha_honeypot_input überschreibt, hat das Problem unter Umständen noch.
Bei reCAPTCHA gibt es zwei bekannte Fallen. Erstens die Konfiguration pro Verkaufskanal: In einem Forenthread nach einem Update von 6.4 auf 6.6.1.1 war reCAPTCHA im Hauptkanal aus, in einem zweiten Kanal aber noch an. Zweitens lädt Shopware ab 6.7.4.0 das reCAPTCHA-Skript erst, wenn die technisch notwendigen Cookies akzeptiert sind. Ohne Skript gibt es kein Token.
Prüfen, ob es Bots sind
Der Shopware-Logeintrag allein verrät das nicht. Das Access-Log schon. Mit dem üblichen Combined-Format zeigt dir das hier alle abgelehnten POSTs auf die drei Routen, gruppiert nach IP:
awk '$6 == "\"POST" && $7 ~ /^\/(account\/register|form\/contact|form\/newsletter)/ && $9 == 403 {print $1}' \
/var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Bots fallen meist auf, weil wenige IPs sehr viele Versuche machen, oft aus Rechenzentrums-Netzen und gern nachts im Sekundentakt. Noch eindeutiger ist ein POST, dem kein GET auf die Formularseite von derselben IP vorausgeht. Das prüfst du für eine verdächtige IP so:
grep '^203.0.113.7 ' /var/log/nginx/access.log | awk '{print $4, $6, $7, $9}' | head -30
Taucht vor dem 403 nie ein GET /account/login oder GET /account/register auf, war es kein Browser. Ein echter Kunde lädt die Seite, dann kommen CSS, JS und Bilder, und erst danach der POST.
Echte Kunden erkennst du umgekehrt: einzelne 403 von IPs mit normalem Seitenverlauf davor, Firefox im User-Agent, und vielleicht Anrufe beim Support, dass „die Registrierung nicht geht“. Ein guter Gegencheck ist auch die Zahl der Neuregistrierungen. Fällt die nach einem Theme-Update ab, während die Captcha-Fehler steigen, solltest du nachsehen.
Die Lösung
Für das Bot-Rauschen gibt es nichts zu reparieren. Die Exception ist nur auf dem falschen Log-Level.
Shopware bringt dafür einen Mechanismus mit: shopware.logger.error_code_log_levels. Damit lässt sich das Level für einen Error-Code herabsetzen, ohne die Exception zu verschlucken. Den Schlüssel gibt es schon in 6.5. In der eigenen config/packages/shopware.yaml:
shopware:
logger:
error_code_log_levels:
FRAMEWORK__INVALID_CAPTCHA_VALUE: notice
Danach:
bin/console cache:clear
Seit 6.7.14.0 steht genau dieser Eintrag schon in der Core-Konfiguration. Ab dieser Version ändert sich auch das Verhalten: Gescheiterte reCAPTCHA-Prüfungen (v2 und v3) erscheinen auf normalen Formularen als Formularfehler statt als 403-Seite, ein fehlendes Token fordert den Kunden zum erneuten Versuch auf. Der Honeypot scheitert laut Release-Info weiterhin mit 403, weil ihn nur Bots auslösen sollen.
Für echte Kunden mit Firefox auf 6.5 oder älterem 6.6: Update auf mindestens 6.6.10.0. Wenn das nicht sofort geht, übernimm das Attribut im eigenen Theme. Das ist ein Workaround, kein Ersatz für das Update:
{% sw_extends '@Storefront/storefront/component/captcha/honeypot.html.twig' %}
{% block component_captcha_honeypot_input %}
<input type="text"
name="{{ constant('Shopware\\Storefront\\Framework\\Captcha\\HoneypotCaptcha::CAPTCHA_REQUEST_PARAMETER') }}"
class="d-none"
value=""
tabindex="-1"
autocapitalize="off"
spellcheck="false"
autocorrect="off"
autocomplete="new-password"
>
{% endblock %}
Bei reCAPTCHA gehst du in den Grundeinstellungen zum Abschnitt Captcha und schaltest oben jeden Verkaufskanal einzeln durch. „Alle Verkaufskanäle“ allein reicht nicht, weil ein Kanal eigene Werte für Aktivierung, Site Key, Secret Key und Schwellwert haben kann.
Wenn du es nicht ganz ausblenden willst
Auch mit notice bleibt der Eintrag im Log, und das ist gut so. Bots und echte Kunden unterscheiden sich vor allem in der Menge und im Muster. Ein paar hundert Treffer pro Nacht von zehn IPs sind Rauschen. Ein gleichmäßiger Anstieg tagsüber, kurz nach einem Theme-Deployment, ist ein kaputtes Formular. Sinnvoll ist deshalb kein Alarm pro Ereignis, sondern einer auf die Rate über einen längeren Zeitraum.
Ob die Captcha-Fehler gerade Bots sind oder deine Registrierung blockieren, siehst du nur, wenn du die Zahl über Tage verfolgst. ShopSignal zählt Fehler und Warnungen pro Shop, macht die Logs per Volltextsuche durchsuchbar und alarmiert über Regeln mit Haltezeit und Cooldown. Ein nächtlicher Bot-Schwall löst dann keinen Alarm aus, ein dauerhafter Anstieg schon.