Hintergründe

reCAPTCHA v2 Callback-Mechanismus: Callbacks finden und auslösen

Sie haben ein gelöstes Token in das Feld g-recaptcha-response geschrieben, doch der Absenden-Button bleibt grau und das Formular reagiert nicht? Dann fehlt der entscheidende Schritt: der Callback. reCAPTCHA v2 meldet einen erfolgreichen Lösungsvorgang nicht über das Textfeld, sondern über eine JavaScript-Funktion, die Google nach Abschluss der Abfrage aufruft. Erst dieser Callback aktiviert die Schaltfläche, validiert das Formular oder startet die AJAX-Anfrage. Wer das Token nur einfügt, aber den Callback nicht auslöst, hinterlässt für die Seite einen Zustand, den sie als „noch nicht gelöst" interpretiert.

Dieser Artikel zeigt, wie Sie den Callback-Namen zuverlässig aus dem DOM ermitteln, wie Sie ihn nach dem Einfügen eines mit CaptchaAI gelösten Tokens auslösen und wie Sie mit den häufigsten Sonderfällen umgehen – von versteckten Closures bis zu Formularen, die ganz ohne data-callback auskommen.


Kurzüberblick

  • Ein befülltes g-recaptcha-response-Feld allein genügt nicht – die Seite reagiert erst auf den Callback.
  • Ermitteln Sie den Callback-Namen dynamisch aus dem DOM, statt ihn fest zu verdrahten.
  • Fehlt ein data-callback, überschreiben Sie grecaptcha.getResponse() oder nutzen Sie den ___grecaptcha_cfg-Fallback.
  • Lösen Sie das Token kurz vor dem Absenden, da es nach rund 120 Sekunden abläuft.

Warum das Formular den eingefügten Token ignoriert

Das reCAPTCHA-Widget und das Formular sind lose gekoppelt. Das Widget schreibt den gelösten Wert in das versteckte Textfeld g-recaptcha-response und ruft anschließend die registrierte Callback-Funktion auf. In diesem Aufruf steckt die eigentliche Geschäftslogik:

  • Er entsperrt den Absenden-Button.
  • Er setzt ein internes State-Flag der Anwendung.
  • Er benachrichtigt ein Framework wie React oder Vue, dass das Formular jetzt gültig ist.

Setzen Sie den Wert nur programmatisch, bleibt dieser Aufruf aus. Die Seite sieht ein befülltes Feld, aber keinen Event, der ihren internen Zustand aktualisiert. Deshalb ist das Auslösen des Callbacks kein optionaler Zusatz, sondern der Kern jeder sauberen reCAPTCHA-v2-Automatisierung.


Wie der Callback im Widget definiert wird

Die Seite registriert den Callback direkt im reCAPTCHA-Widget:

<div class="g-recaptcha"
     data-sitekey="6Le-SITEKEY"
     data-callback="onCaptchaSuccess"
     data-expired-callback="onCaptchaExpired">
</div>

<script>
function onCaptchaSuccess(token) {
  document.getElementById('submit-btn').disabled = false;
  document.getElementById('captcha-token').value = token;
}
</script>

Löst der Nutzer das CAPTCHA, ruft das JavaScript von Google onCaptchaSuccess(token) auf. Die Funktion erhält den Token-String als einziges Argument. Genau diesen Aufruf müssen Sie in Ihrer Automatisierung nachbilden.


Den Callback-Namen im DOM finden

Der Funktionsname ist selten fest verdrahtet – oft wird er dynamisch vergeben. Extrahieren Sie ihn deshalb zur Laufzeit. Drei Wege decken praktisch alle Integrationen ab:

  • über das data-callback-Attribut des Widgets,
  • über den Aufruf von grecaptcha.render() im Seitenquelltext,
  • über das Abfangen der Registrierung in den DevTools.

Weg 1: Das data-callback-Attribut auslesen

// In browser console
const widget = document.querySelector('.g-recaptcha');
const callbackName = widget?.getAttribute('data-callback');
console.log('Callback:', callbackName);

Weg 2: grecaptcha.render() im Seitenquelltext prüfen

Manche Seiten nutzen grecaptcha.render() mit einer Callback-Option statt des Attributs data-callback:

// Search page source for grecaptcha.render
document.querySelectorAll('script:not([src])').forEach(s => {
  if (s.textContent.includes('grecaptcha.render')) {
    console.log(s.textContent.match(/callback\s*:\s*(\w+)/)?.[1]);
  }
});

Weg 3: Die Callback-Registrierung abfangen

Führen Sie dies in den DevTools aus, bevor die Seite lädt (Quellen → Snippets):

const origRender = grecaptcha.render;
grecaptcha.render = function(container, params) {
  console.log('Render callback:', params.callback);
  console.log('Expired callback:', params['expired-callback']);
  return origRender.apply(this, arguments);
};

Callback nach der Token-Injektion auslösen

Sobald Sie Sitekey und Callback-Namen kennen, ist der Ablauf immer gleich:

  1. Token bei CaptchaAI anfordern (in.php, Methode userrecaptcha).
  2. Den Status abfragen, bis das Ergebnis vorliegt (res.php).
  3. Das Token in das Feld g-recaptcha-response schreiben.
  4. Die Callback-Funktion mit dem Token als Argument aufrufen.

Python mit Selenium

import requests
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://example.com/login")

# Extract sitekey and callback
sitekey = driver.find_element(
    By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-sitekey")

callback = driver.find_element(
    By.CSS_SELECTOR, ".g-recaptcha"
).get_attribute("data-callback")

# Mit CaptchaAI lösen
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": sitekey,
    "pageurl": driver.current_url,
    "json": "1",
}).json()
task_id = resp["request"]

token = None
for _ in range(24):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()
    if result["status"] == 1:
        token = result["request"]
        break

# Inject token into textarea
driver.execute_script("""
    document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
""", token)

# Trigger the callback
if callback:
    driver.execute_script(f"window['{callback}'](arguments[0]);", token)
    print(f"Triggered callback: {callback}")
else:
    # Fallback: try ___grecaptcha_cfg
    driver.execute_script("""
        try {
            var widgetId = Object.keys(___grecaptcha_cfg.clients)[0];
            var callback = ___grecaptcha_cfg.clients[widgetId].aa.l.callback;
            if (typeof callback === 'function') callback(arguments[0]);
        } catch(e) {}
    """, token)
    print("Triggered callback via ___grecaptcha_cfg")

Der Fallback über ___grecaptcha_cfg greift dann, wenn kein data-callback-Attribut gesetzt ist, die Funktion aber trotzdem intern registriert wurde. Die verschachtelten Objektschlüssel (aa.l.callback) können sich mit reCAPTCHA-Versionen ändern – prüfen Sie den Pfad im Zweifel einmal manuell in der Konsole.

JavaScript mit Puppeteer

const puppeteer = require('puppeteer');

// After solving and getting the token...
await page.evaluate((token, callbackName) => {
  // Set textarea value
  const textarea = document.querySelector(
    'textarea[name="g-recaptcha-response"]'
  );
  textarea.value = token;
  textarea.style.display = 'block'; // sometimes hidden

  // Trigger callback
  if (callbackName && typeof window[callbackName] === 'function') {
    window[callbackName](token);
    console.log(`Called ${callbackName}()`);
  } else {
    // Fallback: search grecaptcha config
    try {
      const clients = ___grecaptcha_cfg.clients;
      const widgetId = Object.keys(clients)[0];
      const cb = clients[widgetId]?.aa?.l?.callback;
      if (typeof cb === 'function') cb(token);
    } catch (e) {}
  }
}, token, callbackName);

Beispiel: Selenium-Test in GitLab CI

In vielen deutschen Teams laufen Selenium- oder Puppeteer-Tests in GitLab CI gegen eine eigene Staging-Umgebung wie https://staging.example-app.test/login. Genau dort fällt das Problem oft zuerst auf: Der Test füllt Benutzername und Passwort aus, schreibt das Token in das Feld – und scheitert beim Klick auf einen Button, der noch deaktiviert ist. Ergänzen Sie den Callback-Aufruf nach der Injektion, läuft der Test wieder durch, ohne dass Sie an der Anwendung selbst etwas ändern müssen.


Formulare ohne data-callback

Nicht jede Seite definiert einen Callback. Manche prüfen beim Absenden schlicht grecaptcha.getResponse(). Für diese Fälle überschreiben Sie die Funktion so, dass sie Ihr Token zurückgibt:

driver.execute_script("""
    const token = arguments[0];
    document.querySelector('textarea[name="g-recaptcha-response"]').value = token;
    // Override getResponse to return the token
    if (typeof grecaptcha !== 'undefined') {
        grecaptcha.getResponse = function() { return token; };
    }
""", token)

# Then submit the form normally
driver.find_element(By.CSS_SELECTOR, "form").submit()

So liefert die Prüfung beim Absenden den erwarteten Wert, auch ohne registrierten Callback.


Abgelaufene Token und expired-callback

Einige Seiten definieren zusätzlich data-expired-callback, um den Absenden-Button zu deaktivieren, sobald das Token abläuft. Ein reCAPTCHA-v2-Token ist nur rund 120 Sekunden gültig. Zwei Situationen führen hier typischerweise zu Fehlern:

  • Das Token wurde Minuten vor dem Absenden gelöst und ist bereits abgelaufen.
  • Ein expired-callback hat den Button nach dem Ablauf erneut gesperrt.

Die zuverlässige Gegenmaßnahme: den Lösungsvorgang möglichst kurz vor dem Absenden anstoßen, nicht Minuten vorher.

// Check for expired callback
const expiredCallback = document.querySelector('.g-recaptcha')
  ?.getAttribute('data-expired-callback');
console.log('Expired callback:', expiredCallback);

Häufige Probleme und ihre Lösung

Die meisten Fehler beim Callback-Handling lassen sich auf vier Ursachen zurückführen. Diese Tabelle ordnet Symptom, Grund und Gegenmaßnahme einander zu:

Problem Ursache Lösung
Formular bleibt nach der Injektion deaktiviert Callback wurde nicht ausgelöst Callback-Funktion ermitteln und aufrufen
ReferenceError: function not defined Callback in einer Closure definiert Fallback über ___grecaptcha_cfg nutzen
Token eingefügt, aber keine AJAX-Anfrage Callback startet AJAX statt Formular-Submit Prüfen, was die Callback-Funktion tatsächlich tut
Token akzeptiert, Seite zeigt trotzdem Fehler Token vor dem Callback abgelaufen Lösung näher an den Absendezeitpunkt legen

Häufige Fragen

Warum bleibt der Absenden-Button trotz eingefügtem Token deaktiviert?

Weil das Setzen des Textfelds keinen Event auslöst. Die Seite entsperrt den Button erst im Callback. Rufen Sie die Callback-Funktion nach der Injektion explizit auf, dann aktualisiert die Anwendung ihren Zustand und gibt den Button frei.

Wie finde ich den Callback-Namen, wenn er in einer Closure versteckt ist?

Über das Objekt ___grecaptcha_cfg. Lesen Sie den ersten Client-Eintrag aus (Object.keys(___grecaptcha_cfg.clients)[0]) und greifen Sie auf die registrierte Callback-Funktion zu. Da die internen Objektschlüssel sich ändern können, sollten Sie diesen Zugriff immer in ein try/catch kapseln.

Muss ich den Callback bei Selenium und Puppeteer unterschiedlich auslösen?

Nein, das Prinzip ist identisch: Token in g-recaptcha-response schreiben und anschließend die Callback-Funktion mit dem Token als Argument aufrufen. Nur die Ausführungsschicht unterscheidet sich – driver.execute_script(...) bei Selenium, page.evaluate(...) bei Puppeteer.

Wie lange ist ein gelöstes reCAPTCHA-v2-Token gültig?

Etwa 120 Sekunden. Fordern Sie das Token deshalb erst an, wenn Sie das Formular kurz darauf absenden. Bei längeren Abläufen riskieren Sie, dass ein expired-callback das Formular vor dem Absenden wieder sperrt.

Gilt dieser Mechanismus auch für reCAPTCHA v3?

Nein. reCAPTCHA v3 arbeitet mit grecaptcha.execute() und gibt ein Promise zurück. Es gibt kein sichtbares Widget und kein data-callback-Attribut – die Score-Auswertung läuft serverseitig.


reCAPTCHA v2 mit korrektem Callback-Handling lösen

CaptchaAI liefert das Token für reCAPTCHA v2 (Methode userrecaptcha) – das saubere Auslösen des Callbacks übernehmen Sie mit den Snippets aus diesem Artikel. Holen Sie sich Ihren API-Schlüssel unter captchaai.com und lösen Sie Ihr erstes reCAPTCHA v2 in wenigen Minuten.


Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.