Zum Inhalt springen
Antisy

PHP-Formular vor Bots schützen, ohne Rätsel

Setzen Sie ein Script-Tag in die Seite, lesen Sie das versteckte Feld antisy-token, wenn das Formular eintrifft, und senden Sie es mit Ihrem Secret von Ihrem Server an POST /v1/siteverify: Die Antwort ist allow oder deny. Ein paar Zeilen PHP und cURL, ohne Bibliothek.

Stand: .

Was Sie brauchen

  • Eine Website, im Dashboard von Antisy (auf Französisch) angelegt, mit den Hostnamen Ihrer Seiten.
  • Der Site-Key (32 Zeichen): Er ist öffentlich und kommt in die Seite; das Verifizierungs-Secret (64 Zeichen) bleibt auf Ihrem Server und wird nur einmal angezeigt, bei seiner Erstellung.
  • Seiten, die über HTTPS ausgeliefert werden.
  • PHP 8.0 oder neuer mit der Erweiterung cURL: Die Funktion der Dokumentation verwendet curl_init und den Typ mixed.

Siehe: Dokumentation: Schlüssel und Testumgebungen

Ist das heute verfügbar?

Der Zugang erfolgt über eine Warteliste. Die API und das Dashboard sind seit dem 27. Oktober 2026 in Betrieb, gehostet bei Infomaniak in Genf; noch ist kein Kunde in Produktion.

Die Beispiele dieser Anleitung folgen der Version 1 der API, wie die Dokumentation sie beschreibt. Um sie auszuprobieren, ohne ein echtes Formular zu schützen, beschreibt die Dokumentation Testschlüssel; ein Testschlüssel kommt nie in die Produktion.

Siehe: Dokumentation: Testschlüssel · Der Pilot · Der Warteliste beitreten

1. Die Seite: ein Script-Tag und ein Formular

Im <head> der Seite mit dem Formular das Script-Tag mit Ihrem Site-Key. Das Formular ist ein gewöhnliches HTML-Formular per POST an Ihre eigene Website: Sie schreiben kein JavaScript. Das Beispiel sendet an /contact; passen Sie die Adresse an Ihre Datei an, zum Beispiel /contact.php.

page.html
<script src="https://api.antisy.com/v1/i/l.js"
        data-site="VOTRE_CLE_DE_SITE" defer></script>

<form method="post" action="/contact">
  <input name="name" autocomplete="name">
  <input name="email" type="email" autocomplete="email">
  <textarea name="message"></textarea>
  <button>Envoyer</button>
</form>

Beim Absenden hält das Script das Formular zurück, bis es ein Token hat, legt das versteckte Feld antisy-token an, füllt es aus und sendet erneut (höchstens 6 Sekunden). Standardmässig deckt das Script die POST-Formulare ab, deren Zieladresse die Ihrer Website ist.

Siehe: Dokumentation: welche Formulare das Script abdeckt

2. Der Server: die Prüffunktion

Speichern Sie die Funktion der Dokumentation unverändert in antisy.php. Sie liest das Secret aus der Umgebungsvariable ANTISY_SECRET und gibt allow oder deny zurück.

antisy.php
<?php
/**
 * Vérifie le jeton Antisy (mode simple). Rend le verdict : "allow" ou "deny".
 *
 * - jeton vide ou absent : envoyé tel quel, Antisy décide ; jeton non vide inutilisable : "deny", sans appel ;
 * - Antisy ne répond pas (réseau, délai, 5xx) : "allow" et on journalise (fail-open) ;
 * - 429 : ANTISY_ON_RATE_LIMIT ;
 * - tout autre statut que 200 (400, 401, 413, 415...) : erreur de VOTRE appel, journalisée,
 *   tentative non vérifiée : "deny". Jamais "allow".
 */
const ANTISY_ON_RATE_LIMIT = 'allow'; // 'deny' für ein sensibles Formular: Ein Angreifer kann einen 429 auslösen

function antisy_verdict(mixed $token): string
{
    if ($token === null) {
        $token = ''; // fehlendes Feld: wie leer behandelt
    }
    // Leer: unverändert gesendet, Antisy entscheidet (Erfassung an: deny; aus: die Engine entscheidet).
    // Nicht leer: 1 bis 2’048 druckbare ASCII-Zeichen; /D: « $ » duldet keinen abschliessenden Zeilenumbruch.
    if ($token !== '' && (!is_string($token) || !preg_match('/^[\x20-\x7e]{1,2048}$/D', $token))) {
        return 'deny'; // ein Formular-Array, ein zu langes oder präpariertes Token: was Antisy antworten würde
    }

    $ch = curl_init('https://api.antisy.com/v1/siteverify');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 3, // Sekunden; danach fail-open
        CURLOPT_FOLLOWLOCATION => false, // keiner Weiterleitung folgen (das ist der Standard, ausgeschrieben, damit sie niemand einschaltet): Eine 3xx ist ein Fehler IHRES Aufrufs
        CURLOPT_POSTFIELDS => http_build_query([
            'secret' => getenv('ANTISY_SECRET'), // siteverify-Secret; nie fest im Code und nie beim Client
            'response' => $token,
        ]),
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $error = curl_error($ch);
    curl_close($ch);

    // Einziger Fall, in dem durchgelassen wird: Antisy antwortet nicht.
    if ($body === false || $status >= 500) {
        error_log("Antisy injoignable, envoi laissé passer (fail-open): {$error} (HTTP {$status})");
        return 'allow';
    }
    if ($status === 429) {
        error_log('Antisy 429 (voie saturée): ' . ANTISY_ON_RATE_LIMIT);
        return ANTISY_ON_RATE_LIMIT;
    }
    if ($status !== 200) {
        error_log("Antisy HTTP {$status} : erreur de l'appel, à corriger ; tentative non vérifiée");
        return 'deny';
    }
    $result = json_decode($body, true);
    if (is_array($result) && is_bool($result['success'] ?? null)) {
        if ($result['success']) {
            return 'allow'; // die einzige Annahme
        }
        return 'deny'; // success false: Ablehnung (das Ergebnis ist « deny »)
    }
    error_log('Réponse Antisy illisible, tentative non vérifiée');
    return 'deny';
}

  • Ein nicht leeres Token muss die Form eines Tokens haben (1 bis 2’048 druckbare ASCII-Zeichen); sonst gibt die Funktion deny zurück, ohne Antisy aufzurufen.
  • Der Aufruf geht per POST an https://api.antisy.com/v1/siteverify, mit secret und response im Body, nie in der Adresse, und mit einem Zeitlimit von 3 Sekunden.
  • Keiner Weiterleitung wird gefolgt: Eine Antwort 3xx ist ein Fehler Ihres Aufrufs, nie ein Freipass.
  • success ist genau dann true, wenn das Ergebnis allow ist: Das ist die einzige Annahme.

3. Der Handler des Kontaktformulars

Eine vollständige Datei, contact.php, die die Übermittlung ohne Begründung ablehnt, wenn das Ergebnis nicht allow ist:

contact.php
<?php
// contact.php: empfängt das Formular der Seite.
require __DIR__ . '/antisy.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit;
}

if (antisy_verdict($_POST['antisy-token'] ?? '') !== 'allow') {
    http_response_code(403);
    exit('Envoi refusé.'); // allgemeine Ablehnung, ohne Begründung
}

// … die Nachricht speichern oder senden

header('Location: /merci.html', true, 303);
exit;

Das Feld wird mit einem leeren Standardwert gelesen (?? ''): Ist das Script blockiert, fehlt das Feld, und die Funktion sendet ein leeres Token, das Antisy als fehlendes Token liest.

Wohin mit dem Secret?

Die Funktion liest getenv('ANTISY_SECRET'). Setzen Sie die Variable in der Umgebung des Webservers oder von PHP-FPM, zum Beispiel im Pool:

Konfiguration des PHP-FPM-Pools
# Umgebungsvariable des Webservers (hier ein PHP-FPM-Pool): Das Secret kommt weder in die Seite noch ins Repository.
env[ANTISY_SECRET] = VOTRE_SECRET_DE_VERIFICATION

Ein PHP-FPM-Pool leert die Umgebung standardmässig (clear_env): Die Direktive env[...] gibt die Variable an PHP weiter. Schreiben Sie das Secret weder in die Seite noch in eine Datei des Repositorys, und zeigen Sie es nie in einer Antwort oder einem Protokoll an.

Was antwortet die Prüfung?

Die Antwort ist ein JSON-Objekt. success ist genau dann true, wenn verdict den Wert allow hat. Es gibt nur diese zwei Ausgänge, und die Antwort nennt nie den Grund: Auch die Ablehnung, die Sie dem Besucher zeigen, ist allgemein.

Ein Token ist 60 Sekunden gültig und lässt sich nur einmal prüfen. Ein zweiter Aufruf mit demselben Token wird abgelehnt.

Antwort von POST /v1/siteverify (HTTP 200)
{
  "success": true,
  "verdict": "allow",
  "flags": [],
  "error-codes": [],
  "challenge_ts": "2026-09-24T10:00:00Z",
  "hostname": "shop.example",
  "action": "contact",
  "event_id": "evt_01J7X4M2Q9K8ZP3R5T6V7W8Y9A"
}

Siehe: Dokumentation: Anfrage, Antwort und Fehlercodes

Und wenn Antisy nicht antwortet?

Die Funktion lässt die Übermittlung in einem einzigen Fall durch: Antisy antwortet nicht (Netz unterbrochen, Zeitlimit von 3 Sekunden überschritten, Fehler 5xx). Ein Ausfall von Antisy darf Ihr Formular nicht schliessen.

Jeder andere HTTP-Fehler (400, 401, 413, 415) kommt von Ihrem Aufruf: Die Funktion lehnt die Übermittlung ab und protokolliert sie, nie ein Freipass. Ein 429 ist eine Entscheidung, die bei Ihnen liegt: Die Konstante ANTISY_ON_RATE_LIMIT steht standardmässig auf allow; setzen Sie deny für ein sensibles Formular.

Siehe: Dokumentation: Anfrage, Antwort und Fehlercodes

Was passiert, wenn das Script blockiert ist?

Eine CSP-Richtlinie, die das Script verweigert, ein Werbeblocker, eine Seite über HTTP oder ein Besucher ohne JavaScript verhindern, dass das Script läuft. Das Formular wird dann trotzdem gesendet, ohne das Feld antisy-token: Lesen Sie es mit einem leeren Standardwert.

Was folgt, hängt von der Einstellung « Collecte du navigateur » (Erfassung im Browser) der Website ab. Ist die Erfassung an, kommt eine Übermittlung ohne Token nicht durch; ist sie aus, wird das fehlende Token nicht angelastet. Eine neue Website beginnt mit ausgeschalteter Erfassung.

Siehe: Dokumentation: wenn das Script nicht läuft

Was eine Bot-Prüfung nicht ersetzt

Keine Bot-Prüfung ist perfekt, und Antisy nennt keine Erkennungsrate. Sie kommt zu den anderen Abwehrmassnahmen eines Formulars hinzu, etwa der Begrenzung der Anzahl Übermittlungen; sie ersetzt sie nicht.

Häufige Fragen

Wo gehört das Secret von Antisy in einer PHP-Anwendung hin?

In eine Umgebungsvariable des Servers namens ANTISY_SECRET: Die Funktion der Dokumentation liest sie mit getenv. Das Secret kommt nie in die Seite und nie ins Repository; das Dashboard zeigt es nur einmal an, bei seiner Erstellung.

Was tun mit einem PHP-Formular, das ohne Token ankommt?

Lesen Sie das Feld mit einem leeren Standardwert ($_POST['antisy-token'] ?? '') und rufen Sie die Funktion auf: Antisy entscheidet. Ist die Erfassung im Browser an, kommt eine Übermittlung ohne Token nicht durch; ist sie aus, entscheidet die Engine anhand der übrigen Bewertung.

Braucht es eine PHP-Bibliothek, um Antisy aufzurufen?

Nein. Die Prüfung ist ein einziger HTTP-Aufruf per POST, den die Funktion der Dokumentation mit cURL macht. Eine PHP-Bibliothek von Antisy gibt es nicht: Das Server-Kit für Node dient nur dieser Sprache.

Siehe auch: Next.js-Formular vor Bots schützen, ohne Rätsel · WordPress-Formular vor Bots schützen, ohne Plugin · Alle Anleitungen · Dokumentation: Schritt-für-Schritt-Anleitung