Tutorials

Redis für CAPTCHA-Token-TTL-Diagnose in QA-Workflows

Wenn ein QA-Lauf die CAPTCHA-Prüfung besteht und der nächste Sekunden später an genau derselben Stelle scheitert, liegt die Ursache fast immer im Timing – nicht im Solver. Ein CAPTCHA-Ergebnis ist nur wenige Sekunden bis Minuten gültig, und ob Ihr Backend das Token innerhalb dieses Fensters akzeptiert, entscheidet über grün oder rot in der Pipeline. Redis macht dieses Fenster sichtbar: Für jeden Lauf hält es fest, wann ein Ergebnis erzeugt wurde, wann es abläuft und wie Ihr Backend im Grenzfall reagiert.

Damit verschiebt sich die Frage in der QA weg von „Wie verwende ich ein Token erneut?" hin zu „Wie messe ich sauber, wie lange ein Ergebnis gültig ist und ob mein Backend abgelaufene Ergebnisse korrekt zurückweist?". Genau dafür ist Redis mit seinen nativen Ablaufoperationen das passende Werkzeug.

Anwendungsbereich: Dieser Leitfaden beschreibt Diagnose- und Telemetrie-Muster für eigene oder autorisierte QA-Umgebungen. Er dient dazu, Ablaufzeiten und Backend-Reaktionen zu messen – nicht dazu, fremde Schutzsysteme zu umgehen.


Warum Token-Timing QA-Läufe unbemerkt kippt

In vielen Teams ist unklar, warum ein und derselbe Test mal grün und kurz darauf rot ist. Die Kette sieht bis zum letzten Schritt korrekt aus:

  • das Widget wurde korrekt geladen,
  • die CaptchaAI-Aufgabe wurde sauber angelegt,
  • das Ergebnis kam an,
  • aber die Backend-Prüfung erfolgte zu spät oder mit dem falschen Kontext.

Als Faustwert sind reCAPTCHA-Token rund 120 Sekunden gültig, Cloudflare-Turnstile-Token etwa 300 Sekunden. Diese Fenster sind kurz genug, dass eine langsame Fixture, ein retry mit exponentiellem Backoff oder ein verzögerter Assertion-Schritt das Ergebnis kippen lassen – ohne dass im Log ein echter Fehler steht. Redis dient hier als Diagnose-Layer, weil Sie für jeden Lauf dieselben vier Fragen reproduzierbar beantworten können:

  • Wann wurde das Ergebnis angefordert?
  • Wann wurde es erhalten?
  • Wie groß war das Restfenster bis zum Ablauf?
  • Welche Antwort gab Ihr eigenes Backend zurück?

Erst wenn diese vier Werte in einem Datensatz stehen, lässt sich ein „flaky" Test von einem echten Timing-Bug unterscheiden.


Welche Felder gehören in den Redis-Datensatz?

Für die Diagnose genügen wenige, aber saubere Felder. Entscheidend ist, dass Sie Zeitfenster dokumentieren, statt Ergebnisse für andere Seiten oder andere Kontexte weiterzureichen.

Feld Zweck Beispiel
task_id Referenz zur CaptchaAI-Aufgabe 185734920
issued_at Zeitpunkt, an dem die Aufgabe erstellt wurde 2026-04-27T08:15:11Z
received_at Zeitpunkt, an dem das Ergebnis eintraf 2026-04-27T08:15:29Z
expires_at internes QA-Ablauffenster 2026-04-27T08:17:09Z
test_run_id Zuordnung zum QA-Lauf checkout-regression-1042
environment Umgebung staging
verification_result Ergebnis Ihres Backend-Checks accepted / expired / invalid

Ein Hinweis zum Datenschutz: Protokollieren Sie nie das vollständige Token. Für die Diagnose reicht eine kurze Vorschau (token_preview). CI-Logs werden in DACH-Teams oft wochenlang aufbewahrt, und unter der DSGVO sollten Sie ohnehin nur die Daten speichern, die Sie für den QA-Zweck tatsächlich brauchen – prüfen Sie also, welche Felder Ihre Pipeline persistiert.


Redis-Datensatz für Token-Laufzeiten schreiben

Der erste Baustein ist ein kompakter Datensatz mit eigener Ablaufgrenze. Die TTL des Redis-Schlüssels spiegelt dabei das interne QA-Fenster wider, sodass verwaiste Einträge automatisch verschwinden:

from __future__ import annotations

from datetime import datetime, timedelta, timezone
import json
import redis


r = redis.Redis(host="localhost", port=6379, decode_responses=True)


def token_record_key(test_run_id: str) -> str:
    return f"qa:captcha:ttl:{test_run_id}"


def store_ttl_record(*, test_run_id: str, task_id: str, received_token: str, ttl_seconds: int) -> str:
    now = datetime.now(timezone.utc)
    payload = {
        "task_id": task_id,
        "token_preview": received_token[:16],
        "issued_at": now.isoformat(),
        "expires_at": (now + timedelta(seconds=ttl_seconds)).isoformat(),
        "verification_result": "pending",
        "environment": "staging",
    }
    key = token_record_key(test_run_id)
    r.set(key, json.dumps(payload), ex=ttl_seconds)
    return key

Dieses Muster ist bewusst auf Diagnose reduziert. Es speichert nur so viel wie nötig, um QA-Läufe zu analysieren und Ablaufgrenzen reproduzierbar zu testen – nicht mehr.


CaptchaAI-Aufgabe im QA-Lauf abfragen

Damit ein Messpunkt entsteht, brauchen Sie ein echtes Ergebnis. Die folgende Funktion übermittelt eine reCAPTCHA-Aufgabe an CaptchaAI und fragt den Status ab, bis das Token vorliegt. Sie gibt sowohl die task_id als auch das Ergebnis zurück, damit Sie beides im Redis-Datensatz korrelieren können:

import requests
import time


API_KEY = "YOUR_API_KEY"


def submit_and_poll(page_url: str, sitekey: str) -> tuple[str, str]:
    submit = requests.post(
        "https://ocr.captchaai.com/in.php",
        data={
            "key": API_KEY,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": page_url,
            "json": 1,
        },
        timeout=30,
    )
    submit.raise_for_status()
    submit_json = submit.json()
    if submit_json.get("status") != 1:
        raise RuntimeError(submit_json.get("request", "submit failed"))

    task_id = submit_json["request"]
    for _ in range(30):
        time.sleep(5)
        result = requests.get(
            "https://ocr.captchaai.com/res.php",
            params={"key": API_KEY, "action": "get", "id": task_id, "json": 1},
            timeout=30,
        )
        result.raise_for_status()
        result_json = result.json()
        if result_json.get("status") == 1:
            return task_id, result_json["request"]

    raise TimeoutError("CaptchaAI polling timed out")

Im nächsten Schritt dokumentieren Sie das Ergebnis nicht als allgemeines Cache-Objekt, sondern als QA-Messpunkt mit klarer Ablaufgrenze. CaptchaAI rechnet pro Thread ab, nicht pro Lösung – für Diagnoseläufe können Sie also gefahrlos zwei Durchläufe pro Testfall abfragen, ohne pro CAPTCHA zu bezahlen.


Ablaufgrenzen gegen das eigene Backend testen

Ein aussagekräftiger QA-Test besteht in der Regel aus zwei Durchläufen:

  1. sofortige Verifikation direkt nach Eintreffen des Ergebnisses,
  2. absichtlich verzögerte Verifikation kurz vor oder nach dem internen Ablaufgrenzwert.
def verify_in_staging(token: str, test_run_id: str, delay_seconds: int = 0) -> dict:
    if delay_seconds:
        time.sleep(delay_seconds)

    response = requests.post(
        "https://staging.example-app.test/qa/captcha/verify",
        json={
            "token": token,
            "testRunId": test_run_id,
            "environment": "staging",
        },
        timeout=30,
    )
    response.raise_for_status()
    return response.json()

So erkennen Sie, ob Ihre eigene Backend-Implementierung abgelaufene oder verspätete Ergebnisse erwartungsgemäß zurückweist und ob die passenden Fehlercodes sauber im Monitoring auftauchen.

Beispiel aus der Praxis: In vielen DACH-Teams läuft die Regressions-Suite in GitLab CI. Legen Sie dort zwei parallele Jobs an – einen mit delay_seconds=0, einen mit delay_seconds knapp oberhalb Ihres expires_at-Fensters. Der erste Job muss accepted liefern, der zweite expired. Weicht das Verhalten ab, haben Sie den Timing-Bug gefunden, bevor er in Produktion auffällt. Denselben Aufbau können Sie auf einem Hetzner- oder netcup-Runner betreiben, ohne die eigentliche Anwendung zu verändern.


Häufige Probleme bei der TTL-Diagnose

Problem Ursache Lösung
Ergebnisse sind zeitlich nicht nachvollziehbar Zeitstempel fehlen oder weichen ab issued_at, received_at und expires_at in einem Datensatz sammeln
Redis-Einträge verschwinden zu früh TTL zu knapp gewählt internes QA-Fenster dokumentieren und Sicherheitsmarge anpassen
Backend antwortet inkonsistent unterschiedliche Testumgebungen oder Fixtures Testlauf-ID und Umgebung immer mitloggen
Diagnose sagt „accepted", Produktverhalten sagt „failed" Browser- und Backend-Logs sind nicht korreliert Request-ID in Browser-Test und Backend-Log gemeinsam verwenden
Team verwechselt Diagnose mit Wiederverwendung QA-Ziel unklar Dokumentation klar auf Ablaufmessung und Verifikation ausrichten

FAQ

Warum besteht ein QA-Lauf die CAPTCHA-Prüfung und der nächste scheitert?

Fast immer, weil das Token zwischen Erzeugung und Backend-Prüfung abgelaufen ist. Wenn Sie issued_at, expires_at und den Zeitpunkt der Verifikation protokollieren, sehen Sie sofort, ob das Restfenster negativ war – dann ist es ein Timing-Problem, kein Solver-Problem.

Wie lange sind CAPTCHA-Token überhaupt gültig?

Als Richtwert sind reCAPTCHA-Token rund 120 Sekunden gültig, Cloudflare-Turnstile-Token etwa 300 Sekunden. Verlassen Sie sich in QA nicht auf diese Zahlen als Konstante, sondern messen Sie das effektive Fenster pro Testfall – es hängt von der konkreten Konfiguration der Zielseite ab.

Sollte ich das vollständige Token in Redis speichern?

Nein. Für die Diagnose reicht eine kurze Vorschau wie received_token[:16]. Vollständige Token gehören nicht in langlebige CI-Logs, und unter der DSGVO sollten Sie ohnehin nur die Felder persistieren, die der QA-Zweck erfordert.

Wie teste ich gezielt das Verhalten bei einem abgelaufenen Token?

Setzen Sie in verify_in_staging ein delay_seconds knapp oberhalb Ihres internen Ablauffensters. Das Backend muss das Ergebnis dann zuverlässig als expired zurückweisen und einen sauberen Fehlercode liefern – genau dieses Verhalten prüft der Test.

Ist diese Diagnose eine Form der Token-Wiederverwendung?

Nein. Es geht ausschließlich darum, Ablaufgrenzen zu messen und die Reaktion des eigenen Backends zu verifizieren. Token direkt vor der Übermittlung lösen und verwenden bleibt der richtige Weg; die Redis-Datensätze sind reine Telemetrie.


Sichere verwandte Leitfäden

Dokumentieren Sie Ablaufgrenzen und Backend-Reaktionen mit belastbaren QA-Daten – CaptchaAI unterstützt reproduzierbare Verifikationsläufe in eigenen Umgebungen.

Kommentare sind für diesen Artikel deaktiviert.