Googles reCAPTCHA v3 sichert Eingabeformulare gegen eine durch Bots automatisierte Nutzung „unsichtbar“ ab. Mehr hierzu in der Anleitung von Google.
Der Beispiel-Code zum Integrieren wollte bei mir nicht gleich auf Anhieb gelingen, daher diese „Notiz an mich selbst“ in Form eines Blog-Beitrages, wie die erfolgreiche Vorgehensweise funktioniert:
- Für die betreffende WebSite in der Admin-Console von Google die nötigen Einträge vornehmen, also reCAPTCHA v3 Eintrag anlegen, man erhält einen Website-Schlüssel und einen geheimen Schlüssel.
- Der Website-Schlüssel wird im JavaScript-Code der Website (somit im Quellcode sichtbar) an zwei Stellen benötigt werden. Den geheimen Schlüssel hingegen benötigen wir nur im PHP-Backend-Code.
- An jenen stellen, an denen ich
$$YourGoogleWebSiteKey
bzw.$$YourGoogleSecretKey
als Platzhalter hier verwende, sind die persönlichen Keys die in der Google Admin-Console angezeigt werden zu verwenden. - In der Website auf der sich das Formular befindet, in der
<head>
Section das Script einfügen.
<script src="https://www.google.com/recaptcha/api.js?render=$$YourGoogleWebSiteKey"></script>
- Im
<body>
unterhalb des Formular-Ende-Tags</form>
folgendes Script einfügen:
<script>
function RenewCaptcha() {
grecaptcha.ready(function() {
grecaptcha.execute('$$YourGoogleWebSiteKey', {action: 'submit'}).then(function(token) {
console.log("Set Captcha Token: " + token);
document.getElementById("captcha").value = token;
});
});
}
</script>
- Und bei den Formularfeldern, z.B. vor dem Submit-Button folgendes versteckte Feld einfügen:
<input type="hidden" id="captcha" name="captcha"/>
- Die Funktion „RenewCaptcha()“ generiert den Code und fügt ihn ins versteckte Feld ein. Damit diese getriggert wird – möglichst kurz vor dem eigentlichen versenden – hinterlegen wir den Aufruf im Submit-Button als OnMouseEnter Event:
<button type="submit" OnMouseEnter="RenewCaptcha()">Nachricht senden</button>
- Ob die Änderungen am Formular funktioniert haben erkennt man nun bereits, indem in der F12-Konsole des Browsers eine Log-Zeile
"Set Captcha Token: ......"
generiert werden muss, sobald man sich mit der Maus über den Submit-Button bewegt. - Hinweis: Der ReCAPTCHA Code ist nur ca. 2 Minuten gültig, daher ist ein Aufruf der
RenewCaptcha()
Funktion direkt beim Laden des Formulars nicht ausreichend – der Anwender könnte längere Zeit brauchen, um das Formular zu befüllen. Umgekehrt hat bei mir das Hinterlegen im OnClick-Event nicht geklappt, da das Formular dann bereits submittet wird, noch bevor die Aktualisierung des Captcha-Codes erfolgt
- Das PHP-Backend – basierend auf einem PHPMailer – ist wie folgt zu ergänzen:
if(!isset($_POST['captcha'])){
# Google Captcha V3 muss mit übergeben werden
$err = true;
$errortext .= "Die Captcha V3 Prüfung ist leer, Sie wurden als Roboter identifiziert!<br/>\n";
} else {
$captcha = $_POST['captcha'];
$secretKey = "$$YourGoogleSecretKey";
$ip = $_SERVER['REMOTE_ADDR'];
$url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . urlencode($secretKey) . '&response=' . urlencode($captcha);
$response = file_get_contents($url);
$responseKeys = json_decode($response,true);
if(!$responseKeys["success"]) {
$err = true;
$errortext .= "Die Captcha V3 Prüfung wurde nicht bestanden, Sie wurden als Roboter identifiziert!<br/>\n";
}
}
- Das eigentliche Versenden des Formulars bindet man dann an eine Prüfung der Variable
$err
Mein fertiges Beispiel ist unter https://hitco.at/kontaktformular-demo/ zu finden.
Danke für die verständliche Darstellung und das gelungene Testbeispiel.
Danke für das gute Beispiel. Wie ich allerdings feststellte, löst die Captchageneration auf einem Touchbildschirm nicht aus, da der Submit Button unmittelbar per Touch angeklickt wird, ohne im Vergleich zur Maus diesen zuerst zu betreten. Funktioniert also meiner Meinung nach ohne weitere Anpassungen nicht mit mobilen Geräten ohne klassische Maus.
reCaptcha V3 benutzt kein sichtbares Captcha sondern registriert die Tastenanschläge bei der Befüllung des Formulars. Mein Beispiel hier funktioniert selbstverständlich auch mit Touch-Geräten wie Smartphones.
Sehr inspirierend, vielen Dank! Mit der Methode könnte man eventuell sogar auf das reCaptcha von Google verzichten?!? Vielleicht nicht zu Ende gedacht, aber so (mit JS /PHP): Jedes betretene und mit Änderung verlassene Pflichtfeld eines Formulars zählt einen Zähler hoch. Sobald ein definierter Mindest-Zählerstand erreicht ist, wird ein Zufallsschlüssel erzeugt, in das ‚hidden‘ captcha-Feld geschrieben und in einem Cookie gespeichert. Man könnte den ‚Absenden‘-Button auch erst danach sichtbar machen. Beim Empfang der Formulardaten wird der Wert aus dem captcha-Feld mit dem Wert des Cookies verglichen und damit entschieden, ob und wie es weitergeht.
Sehr gute Lösung. Schnell und einfach erklärt. So muss es sein.
Funktioniert sogar auf PHP 8.2, was erst im letzten Monat (Januar 2023) rausgekommen ist.
Vielen Dank dafür!!!
Hallo,
vielen dank für die ausführliche Erklärung.
Leider erhalte ich in der Console folgende Meldudung wenn ich mit der Maus über den Button fahre.
Und ich kann mir nicht erklären was ich falsch mache.
Der Code ist identisch…
Vll kann mir jemand helfen?
Uncaught (in promise) TypeError: JSON.stringify is not a function
at Array. (recaptcha__de.js:163:350)
Meine Ausführungen und mein hier verlinktes Demo-Formular funktionieren unverändert einwandfrei. Wenn du uns die URL Deiner Formularseite verrätst kann ich einen Blick darauf werfen.
Guten Morgen Gunnar,
erstmal vielen Dank, dass du so schnell geantwortet hast 🙂
Hier der Link:
https://www.anne-sigmund.de/kontakt.php
Ich bin über jede Hilfe dankbar – kämpfe seit Tagen mit der reCpatcha Integration 🙁
Es liegt an deiner eingebundenen „js/lazyload-min.js“ … wenn du dieses Script nicht lädst funktioniert ReCaptcha-Token-Generierung. Vermutlich kollidieren da Funktions- oder Variablen-Namen mit dem Google-Script.
Danke, ich werde es gleich testen!
Hi Gunnar, es hat geklappt.
Ich danke dir vielmals! 🙂
Das eigentliche Versenden des Formulars bindet man dann an eine Prüfung der Variable $err
Genau da hänge ich jetzt.
Wie?
Indem man die Funktion zum eigentlichen Versenden der Mail (z.B.
$mail->send();
um beim Beispiel PHPMailer zu bleiben) nur dann aufruft, wenn $err eben nicht auf true gesetzt ist.