Dokumentation

CaptchaCore in 5 Minuten in dein Projekt integrieren.

1 Site & Keys erstellen

  1. Login unter /admin
  2. Unter Sites eine neue Site anlegen (Name + Domain)
  3. Unter API-Keys ein Key-Paar generieren
  4. Den cc_pub_... (public) und cc_sec_... (secret) Key sicher speichern

2 Widget einbinden (Frontend)

Der public key (cc_pub_) wird im Browser verwendet.

<!-- In deinem HTML, vor </body> -->
<script src="https://src-eu.captchacore.eu/widget/captchacore-v2.min.js"
        data-service-url="https://api.captchacore.eu"
        data-site-key="cc_pub_DEIN_KEY"
        async defer></script>

<!-- Formular mit CaptchaCore -->
<form data-captchacore="interactive" method="post">
    <!-- Deine Formularfelder -->
    <div data-captchacore-widget></div>
    <button type="submit">Absenden</button>
</form>

3 Token verifizieren (Backend)

Der secret key (cc_sec_) wird serverseitig verwendet.

POST https://api.captchacore.eu/api/v2/verify
Header: X-CaptchaCore-Key: cc_sec_DEIN_SECRET

Body:
{
    "token":     "<captchacore_token aus dem Formular>",
    "form_type": "register"
}

Response:
{
    "valid":      true,
    "decision":   "allow",        // allow | challenge | step_up | block
    "risk_score": 8,
    "confidence": 0.94,           // 0.0–1.0 Signalabdeckung
    "reasons":    ["pow_valid", "behavior_human_like", "ip_clean"],
    "step_up":    null             // Step-Up-Challenge falls noetig
}

HTML Integration

Für statische Seiten, PHP-Projekte, Node.js oder jedes andere Backend.

Frontend

Das Widget wird automatisch an alle Formulare mit data-captchacore Attribut angeheftet. Es fügt ein verstecktes captchacore_token Input-Feld ein.

Backend (beliebige Sprache)

# curl Beispiel
curl -X POST https://api.captchacore.eu/api/v2/verify \
  -H "X-CaptchaCore-Key: cc_sec_DEIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"token":"TOKEN_AUS_FORMULAR","form_type":"contact"}'

# Antwort prüfen: valid=true UND action != "block"

Laravel Integration

1. Repository eintragen

Füge das CaptchaCore-Repository in deine composer.json ein:

{
    "repositories": [
        {
            "type": "composer",
            "url": "https://captchacore.eu/packages"
        }
    ]
}

2. Paket installieren

composer require captchacore/laravel

3. .env konfigurieren

CAPTCHACORE_URL=https://captchacore.eu
CAPTCHACORE_SITE_KEY=cc_pub_dein_key
CAPTCHACORE_SECRET_KEY=cc_sec_dein_secret
CAPTCHACORE_WIDGET_MODE=interactive  # interactive | visible | invisible
CAPTCHACORE_WIDGET_THEME=auto        # auto | light | dark
CAPTCHACORE_WIDGET_COLOR=#4ade80
CAPTCHACORE_WIDGET_LABEL=Ich bin kein Bot
CAPTCHACORE_WIDGET_BRAND=CaptchaCore

Blade-Component

<form method="post" data-captchacore="interactive">
    @csrf
    <!-- Formularfelder -->
    <x-captchacore::widget />
    <button type="submit">Absenden</button>
</form>

Middleware

// Route schützen
Route::post('/register', RegisterController::class)
    ->middleware('captchacore:register');

// Oder als Validation Rule
'captchacore_token' => ['required', new CaptchaCoreToken('contact')]

// Tests: fake() mockt alle Verifikationen
CaptchaCore::fake();

Symfony Bundle

Für Symfony 6.4 LTS und 7.x. Vier Integrationswege: FormType, Validator-Constraint, Controller-Attribute und Security-Badge.

1. Installieren

composer require captchacore/captchacore-bundle

Symfony Flex registriert das Bundle und legt config/packages/captchacore.yaml automatisch an.

2. .env konfigurieren

CAPTCHACORE_URL=https://api.captchacore.eu
CAPTCHACORE_SITE_KEY=cc_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
CAPTCHACORE_SECRET_KEY=cc_sec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

FormType (empfohlen)

use CaptchaCore\SymfonyBundle\Form\Type\CaptchaCoreType;

$builder
    ->add('email', EmailType::class)
    ->add('message', TextareaType::class)
    ->add('captcha', CaptchaCoreType::class, [
        'form_type' => 'contact',
    ]);

Controller-Attribute

use CaptchaCore\SymfonyBundle\Security\Attribute\RequiresCaptcha;

#[Route('/contact', methods: ['POST'])]
#[RequiresCaptcha(formType: 'contact')]
public function submit(Request $request): Response { /* ... */ }

Validator-Constraint (DTOs)

use CaptchaCore\SymfonyBundle\Validator\CaptchaCoreToken;

final class ContactDto {
    public function __construct(
        #[Assert\NotBlank]
        public string $email,

        #[CaptchaCoreToken(formType: 'contact')]
        public string $captchaToken,
    ) {}
}

Programmatisch

use CaptchaCore\SymfonyBundle\Client\CaptchaCoreClient;

$result = $this->captcha->verify($token, 'login');

if ($result->blocked()) {
    throw new AccessDeniedHttpException();
}

// Properties: valid, riskScore, action, confidence, reasons, stepUp

Twig (standalone)

{{ captchacore_widget(mode: 'interactive') }}

Tests

CaptchaCoreClient::fake(VerificationResult::allow());
$this->client->request('POST', '/contact', [...]);
self::assertResponseIsSuccessful();

WordPress Plugin

  1. Plugin installieren — ZIP hochladen oder Ordner nach wp-content/plugins/ kopieren
  2. Aktivieren unter Plugins
  3. Einstellungen > CaptchaCore öffnen
  4. Service-URL, Site Key und Secret Key eintragen
  5. Widget-Modus, Theme und Farbe wählen
  6. Gewünschte Formulare aktivieren (Login, Register, Kommentare, etc.)

Kein Code nötig. Das Plugin bindet das Widget automatisch in alle aktivierten WordPress-Formulare ein und verifiziert Tokens serverseitig.

API Referenz

GET /api/v2/challenge

PoW-Challenge anfordern. Auth: X-CaptchaCore-Key: cc_pub_...

Response: { "nonce": "a3f8...", "difficulty": 4, "algorithm": "sha256", "expires_at": 1712345678 }

POST /api/v2/verify

Token verifizieren. Auth: X-CaptchaCore-Key: cc_sec_...

Body:     { "token": "...", "form_type": "register" }
Response: { "valid": true, "risk_score": 8, "action": "allow" }

Antwort immer HTTP 200. Prüfe valid und action.

GET /api/v2/status

Health-Check + Under-Attack-Status. Auth: cc_sec_...

Response: { "status": "ok", "under_attack": false, "version": "1.0.0" }

Widget Optionen

Das Widget wird über data- Attribute auf dem Container konfiguriert:

Attribut Werte Standard Beschreibung
data-theme auto | light | dark auto Farbschema
data-color Hex-Farbe #34d399 Akzentfarbe für Checkbox und Rahmen
data-label Text Ich bin kein Bot Checkbox-Beschriftung
data-brand Text oder leer CaptchaCore Branding rechts, leer = versteckt
data-size normal | compact normal Widget-Grösse

Widget Modi

Der Modus wird über data-captchacore="..." auf dem <form> gesetzt.

interactive Empfohlen

Widget immer sichtbar. PoW löst automatisch. Checkbox setzt sich selbst wenn genug menschliche Interaktion erkannt wird (3+ Zeichen getippt, Mausbewegung, 2+ Sekunden). Kein Klick nötig für echte User. Fallback: manueller Klick.

visible

Widget sichtbar. User muss die Checkbox klicken um die Verifikation zu starten. Ähnlich wie reCAPTCHA v2.

invisible

Kein sichtbares UI. PoW löst komplett im Hintergrund. Token wird automatisch beim Submit eingefügt. Ideal wenn kein visueller Schutz gewünscht ist.

Adaptive Risk Engine

Der Risk-Score ist die gewichtete Summe aller Penalties (0-100). Die Schwellwerte werden durch das aktive Site-Profil und die Form-Policy bestimmt. Die V2 Response enthält zusätzlich einen Confidence-Score (0.0-1.0) und maschinenlesbare Reason-Codes.

Score Aktion Bedeutung
0-29allowMenschlich, durchlassen
30-59challengeVerdacht, wird durchgelassen aber geloggt
60-100blockBot erkannt, blockieren

Penalty-Faktoren

pow_failed:          +100   PoW falsch oder fehlend
nonce_reused:        +100   Replay-Angriff
ip_blocked:          +100   IP auf Blockliste
rate_limit_hit:       +80   Zu viele Requests
user_agent_headless:  +70   Puppeteer/Playwright
tor_exit:             +60   TOR Exit Node
behavior_bot:         +50   Verhalten bot-typisch
time_too_fast:        +35   Submit < 1.5 Sekunden
asn_hosting:          +25   Rechenzentrum-ASN
behavior_suspicious:  +20   Verhalten verdächtig

V2.1 Advanced Bot Detection

Zusätzliche Signale die selbst getarnte Headless-Browser entlarven. Alle Prüfungen sind kontextsensitiv: Mobile Geräte und Privacy-Browser (Safari/Firefox) werden korrekt als legitim erkannt und nicht falsch geflaggt.

env_automation_globals: +90   Selenium/Puppeteer/CDP Leak im window-Objekt
env_ua_brand_bot:       +80   UA Client Hints verraten Automation-Framework
env_software_renderer:  +50   WebGL nutzt SwiftShader/llvmpipe = headless
env_canvas_too_fast:    +40   Canvas-Render < 2ms = headless (Mensch: 5-50ms)
env_no_storage:         +30   localStorage UND indexedDB fehlen
env_raf_robotic:        +25   requestAnimationFrame perfekt uniform (CV < 0.05)
env_no_webgl_ext:       +25   0 WebGL-Extensions auf Desktop = headless
header_no_sec_fetch:    +25   Sec-Fetch-* Header fehlen = altes/scripted Client
header_no_accept_lang:  +20   Accept-Language fehlt = scripted
env_no_hover:           +5    Hover nicht unterstützt (selten valide auf Desktop)
env_audio_hash_missing: +5    OfflineAudioContext blockiert (Privacy-Browser ok)

V2.1 erhöht die Erkennungsrate von Stealth-Frameworks (Playwright Stealth, Puppeteer Stealth) deutlich, ohne legitime Nutzer zu beeinträchtigen.

Site-Profile & Form-Policies

Die V2 Risk Engine bewertet kontextabhängig: Login-Formulare werden strenger bewertet als Kontaktformulare. Site-Profile definieren die Grundhaltung, Form-Policies überschreiben gezielt pro Formular-Typ.

Site-Profile (Presets)

Jede Site wird einem Profil zugeordnet. Das Profil bestimmt Schwellen, Challenge-Typen und Behavioral-Gewichtungen.

Profil Allow < Challenge < Difficulty Einsatz
Low Friction 40 70 2 Newsletter, Kommentare, Info-Seiten
Balanced 30 60 4 Standard für die meisten Formulare
Auth Hardened 20 50 5 Login, Registrierung, Passwort-Reset
High Security 15 40 6 Checkout, Admin-Aktionen, Bezahlung

Form-Policies

Form-Policies überschreiben das Site-Profil für bestimmte Formular-Typen. Der form_type wird beim Verify-Request mitgeschickt.

form_type Profil Allow < Challenge < Fail-Mode
login Auth Hardened 20 50 fail_closed
register Auth Hardened 20 50 fail_closed
password_reset High Security 15 40 fail_closed
checkout High Security 15 40 fail_closed
contact Balanced 30 60 fail_open
comment Low Friction 35 65 fail_open
newsletter Low Friction 40 70 fail_open

Merge-Reihenfolge

Spätere Ebenen überschreiben frühere:

1. Globale Defaults     (config: allow<30, challenge<60)
2. Site-Profil           (z.B. "Auth Hardened" → allow<20, challenge<50)
3. Form-Policy           (z.B. login → allow<15, fail_closed)

Integration (Entwickler)

Der form_type wird beim Widget-Einbau und beim Verify-Request angegeben:

<!-- Widget: form_type als data-Attribut -->
<form data-captchacore="interactive" data-form-type="login">
    <div data-captchacore-widget data-form-type="login"></div>
</form>

// Verify-Request: form_type im Body
POST /api/v2/verify
{
    "token":     "...",
    "form_type": "login"   // Steuert welche Policy angewendet wird
}

Verfügbare Typen: login, register, contact, comment, checkout, password_reset, newsletter, custom. Ohne form_type werden die globalen Defaults verwendet.

Fehlerbehandlung & Fail-Open

Wenn der CaptchaCore-Service nicht erreichbar ist, kann das Verhalten konfiguriert werden:

Fail-Open (Standard)

Formulare werden trotzdem durchgelassen. Verfügbarkeit > Sicherheit bei Ausfall. Empfohlen für die meisten Sites.

CAPTCHACORE_FAIL_OPEN=true

Fail-Closed

Formulare werden blockiert wenn der Service nicht antwortet. Nur für hochkritische Systeme.

CAPTCHACORE_FAIL_OPEN=false

Alle API-Calls haben einen Timeout von 3 Sekunden. Bei Timeout greift die Fail-Open/Closed-Konfiguration.

Keys & Rotation

Jede Site hat ein Key-Paar: Site Key (public, im Browser) und Secret Key (geheim, nur Server).

Key-Formate

cc_pub_6e65b5c4597a688c93f3be6e345ee1a5...  ← 7 + 64 hex = 71 Zeichen
cc_sec_8bb043aa6424a1fdb3e048d2d3232894...  ← 7 + 64 hex = 71 Zeichen

Key-Rotation

Bei Rotation wird der alte Key in den Status rotated versetzt und bleibt noch 24 Stunden gültig. So können Rolling Deployments ohne Downtime durchgeführt werden.

active — aktueller Key, wird für neue Requests verwendet
rotated — alter Key, noch 24h gültig (konfigurierbar)
revoked — sofort ungültig, keine Requests mehr möglich

Secret Key Sicherheit

  • Secret Key wird als SHA-256 Hash in der Datenbank gespeichert
  • Klartext wird nur einmalig nach Erstellung angezeigt
  • Kann nicht wiederhergestellt werden — bei Verlust: neuen Key generieren
  • Wird niemals in Logs, API-Responses oder Fehlermeldungen ausgegeben

Under-Attack-Mode

Bei einem Angriff (hohe Block-Rate) kann der Schutz verstärkt werden:

Automatische Aktivierung

Wenn über 70% aller Requests in einem 5-Minuten-Fenster blockiert werden, aktiviert sich der UAM automatisch.

# Konfigurierbar in den globalen Einstellungen
uam.auto_threshold:        0.70     # 70% Block-Rate
uam.window_minutes:        5
uam.auto_deactivate_after: 3600     # 1 Stunde
uam.cookie_ttl_minutes:    30

Im UAM erhöht sich die PoW-Schwierigkeit um +1 Stufe. Verifizierte Besucher erhalten ein HMAC-signiertes Access-Cookie (HttpOnly, SameSite=Strict).

Backend-Beispiele

Der Verify-Endpoint kann von jedem Backend aufgerufen werden. Hier Beispiele für gängige Sprachen:

PHP (ohne Framework)

$token = $_POST['captchacore_token'] ?? '';

$ch = curl_init('https://api.captchacore.eu/api/v2/verify');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 3,
    CURLOPT_HTTPHEADER     => [
        'X-CaptchaCore-Key: cc_sec_DEIN_SECRET',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'token'     => $token,
        'form_type' => 'contact',
    ]),
]);
$result = json_decode(curl_exec($ch), true);

if (!$result['valid']) {
    die('Bot erkannt.');
}

Node.js

const token = req.body.captchacore_token;

const res = await fetch('https://api.captchacore.eu/api/v2/verify', {
  method: 'POST',
  headers: {
    'X-CaptchaCore-Key': process.env.CAPTCHACORE_SECRET_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ token, form_type: 'register' }),
});
const { valid, risk_score, action } = await res.json();

if (!valid) return res.status(403).json({ error: 'Bot detected' });

Python

import requests

result = requests.post(
    'https://api.captchacore.eu/api/v2/verify',
    headers={'X-CaptchaCore-Key': 'cc_sec_DEIN_SECRET'},
    json={'token': token, 'form_type': 'login'},
    timeout=3,
).json()

if not result['valid']:
    raise Exception('CaptchaCore verification failed')

Troubleshooting

Widget lädt nicht / 500 auf /api/v2/challenge

Domain prüfen: Ist die Domain in den Site-Einstellungen unter "Erlaubte Domains" eingetragen? Wildcards möglich: *.example.com

CORS-Fehler im Browser

Der CaptchaCore-Service muss die anfragende Domain kennen. Domain in den Site-Einstellungen hinzufügen. Widget-JS und Worker-JS werden mit Access-Control-Allow-Origin: * ausgeliefert.

Token ist null / leer

Das Widget-Script muss vor dem Formular-Submit geladen sein. Prüfe: Ist data-captchacore auf dem <form>? Ist data-captchacore-widget Container vorhanden (für visible/interactive)?

PoW FAIL / Nonce FAIL

Der Nonce ist 5 Minuten gültig und einmalig verwendbar. Wenn der User zu lange wartet oder doppelt absendet, schlägt die Verifikation fehl. Seite neu laden.

Checkbox-Haken nicht sichtbar

Externe CSS-Frameworks können das SVG überschreiben. Das Widget nutzt !important für alle kritischen Styles. Bei Problemen: Browser-DevTools > Elements > Computed Styles prüfen.

Risk Score zu hoch für echte User

Behavioral-Schwellwerte prüfen: time_to_submit_ms unter 1500ms = +35 Penalty. User muss genug Zeit auf der Seite verbringen und interagieren.

Infrastruktur & Domains

Domain-Architektur

Domain Zweck Typ
api.captchacore.euChallenge, Verify, Status, VersionAPI (JSON)
src-eu.captchacore.euWidget JS + Worker (EU-only CDN)CDN (Standard)
src.captchacore.euWidget JS + Worker (Globales CDN)CDN (optional)
captchacore.euWebsite, Admin-Panel, DocsFrontend

Content Security Policy (CSP)

Wenn Sie auf Ihrer Website eine Content Security Policy verwenden, müssen folgende Domains erlaubt werden:

# Minimal (EU-only CDN + API)
script-src  https://src-eu.captchacore.eu;
connect-src https://api.captchacore.eu https://src-eu.captchacore.eu;
worker-src  blob:;

# Mit globalem CDN (zusätzlich)
script-src  https://src.captchacore.eu;
connect-src https://src.captchacore.eu;

Warum diese Einträge?

  • script-src — Das Widget-JS (captchacore-v2.min.js) wird vom CDN geladen
  • connect-src (API) — Das Widget ruft /api/v2/challenge per fetch() auf
  • connect-src (CDN) — Der PoW-Worker wird per fetch() vom CDN nachgeladen
  • worker-src blob: — Der Worker wird als Blob-URL instanziiert (vermeidet CORS)

CORS-Header

CaptchaCore setzt automatisch die richtigen CORS-Header auf allen API-Endpoints. Sie müssen auf Ihrer Seite keine CORS-Konfiguration vornehmen. Die API antwortet mit:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Accept, X-CaptchaCore-Key

NGINX-Beispiel für Ihre Webseite

Falls Sie eine strenge CSP auf Ihrer Webseite konfiguriert haben:

# NGINX — CSP für CaptchaCore Widget
add_header Content-Security-Policy
  "script-src 'self' https://src-eu.captchacore.eu;
   connect-src 'self' https://api.captchacore.eu https://src-eu.captchacore.eu;
   worker-src 'self' blob:;"

Datenschutz (DSGVO)

Was gespeichert wird

  • IP-Hash (täglicher Salt)
  • Anonymisierte IP (letztes Oktett 0)
  • Ländercode (2 Buchstaben)
  • User-Agent-Hash
  • Risk-Score + Ergebnis
  • Automatische Löschung (Standard: 30 Tage)

Was NICHT gespeichert wird

  • Keine IP-Klartexte
  • Keine Cookies (außer UAM)
  • Keine Maus-Trajektorien
  • Keine Tastatureingaben
  • Keine Formulardaten
  • Keine externen Requests