GeeTest v3 in PHP lösen und das Ergebnis zurücksenden

solve geetest in php - How to Solve GeeTest v3 in PHP and Post the Result Back

Der meiste Captcha-Code geht von einem Token rein und einem Token raus aus. GeeTest passt da nicht hinein. Eine Lösung liefert drei Werte, die zusammen gepostet werden müssen, und die Challenge, die Sie eingespeist haben, läuft etwa sechzig Sekunden nach ihrer Ausstellung ab. In PHP, wo Arbeit oft in eine Warteschlange geschoben wird, verursacht dieser Ablauf eine ganz bestimmte und sehr verwirrende Art von Fehler.

Zwei Eingaben, die sich unterschiedlich verhalten

WertLifetime
gtStatisch pro Website. Kann gecacht werden
challengeEinmalige Verwendung, nach etwa 60 Sekunden tot

Beide stammen vom eigenen GeeTest-Init-Endpunkt der Website. Der gt identifiziert die Website; der challenge gilt pro Versuch und verfällt schnell.

Einrichtung

# PHP 8.0+, with the curl and json extensions.
composer require capskip/capskip
use CapSkip\CapSkip;

$solver = new CapSkip([
    'host' => '127.0.0.1',
    'port' => 8080,
    'recaptchaTimeout' => 300,   // GeeTest uses this, not defaultTimeout
]);

defaultTimeout regelt nur Bild-Captchas. Alles Interaktive, GeeTest eingeschlossen, verwendet recaptchaTimeout.

Wird gelöst

$result = $solver->geetest(
    '81388ea1fc187e0c335c0a8907ff2625',   // gt, static per site
    '7cf6a8b1a2c34d5e6f7089abcdef0123',   // challenge, fetched seconds ago
    'https://example.com/login'
);

echo $result['challenge'];
echo $result['validate'];
echo $result['seccode'];

Diese drei Schlüssel sind die Antwort. $result['code'] wird ebenfalls befüllt, aber bei GeeTest enthält er den rohen JSON-String statt etwas Übermittelbarem, sodass ein gewohnheitsmäßiges Herausgreifen zu einem rätselhaften Fehlschlag führt.

Verwenden Sie den challenge der zurückkommt, nicht der, den Sie gesendet haben. Sie sind nicht immer gleich.

Der gesamte Ablauf

use CapSkip\CapSkip;

$solver = new CapSkip();
$login = 'https://example.com/login';

// 1. Fresh pair, cache-busted. Cached init responses return spent challenges.
$init = json_decode(file_get_contents(
    'https://example.com/geetest/init?t=' . (int) (microtime(true) * 1000)
), true);

// 2. Solve immediately. Nothing slow between here and step 1.
$result = $solver->geetest($init['gt'], $init['challenge'], $login);

// 3. Post all three together with the real form fields.
$ch = curl_init($login);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'geetest_challenge' => $result['challenge'],
        'geetest_validate' => $result['validate'],
        'geetest_seccode' => $result['seccode'],
        'username' => '...',
        'password' => '...',
    ]),
]);
$response = curl_exec($ch);
curl_close($ch);

Der Cache-Busting-Parameter leistet echte Arbeit. GeeTest-Init-Endpunkte werden oft von einem CDN oder Reverse Proxy zwischengespeichert, und eine zwischengespeicherte Antwort übergibt Ihnen eine Challenge, die jemand bereits verbraucht hat.

Diese drei Feldnamen sind die übliche GeeTest-v3-Konvention, aber eine Website kann sie umbenennen, prüfen Sie also zuerst das tatsächliche Formular.

Die Warteschlangenfalle, die in PHP schlimmer ist

PHP-Anwendungen setzen stark auf Job-Queues, und GeeTest verträgt sich schlecht mit dem naheliegenden Design. Das Versenden eines Jobs, der einen gt und challenge sieht vernünftig aus und scheitert, sobald die Warteschlange irgendeinen Rückstau hat:

// WRONG: the challenge expires while the job waits to be picked up.
$init = fetchGeetestPair();
dispatch(new SolveCaptchaJob($init['gt'], $init['challenge'], $url));

Rufen Sie das Paar stattdessen innerhalb des Workers ab, sodass die Challenge nur Sekunden alt ist, wenn sie verwendet wird:

// RIGHT: the job fetches its own pair at the moment it runs.
class SolveCaptchaJob
{
    public function handle(CapSkip $solver): array
    {
        $init = fetchGeetestPair();   // called inside the worker
        return $solver->geetest($init['gt'], $init['challenge'], $this->url);
    }
}

PHP wird synchron ausgeführt und AsyncCapSkip ist hier nur ein Alias, daher entsteht Parallelität durch das Ausführen von mehr Workern und nicht durch das SDK.

Fehler

use CapSkip\Exceptions\ValidationException;
use CapSkip\Exceptions\NetworkException;
use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\TimeoutException;

try {
    $result = $solver->geetest($gt, $challenge, $pageUrl);
} catch (ValidationException $e) {
    // missing gt or challenge
} catch (NetworkException $e) {
    // CapSkip is not running
} catch (ApiException $e) {
    // usually a challenge that expired before the solve finished
} catch (TimeoutException $e) {
    // exceeded recaptchaTimeout
}

Die meisten GeeTest-Fehlschläge kommen als ApiException und bedeuten, dass die Challenge gestorben ist. Ein erneuter Versuch mit demselben Paar gelingt nie, weil eine verbrauchte Challenge nicht wieder gültig wird.

Häufig gestellte Fragen

Warum ist $result[‘code’] nicht verwendbar?

Weil die Antwort aus drei Werten statt einem besteht. code behält das rohe JSON zur Vollständigkeit, während das SDK die nützlichen Teile erweitert zu challenge, validate und seccode. Senden Sie diese drei.

Kann ich die Challenge in der Datenbank speichern?

Nein. Er ist zur einmaligen Verwendung und läuft nach etwa einer Minute ab, sodass alles, was ihn speichert, einen toten Wert an das übergibt, was ihn wieder ausliest. Cachen Sie den gt wenn Sie möchten; niemals die Challenge.

Deckt dies GeeTest v4 ab?

The geetest Methode zielt auf v3, das Slide-Puzzle, aufgebaut auf einem gt und challenge Paar. v4 hat das Parametermodell geändert, prüfen Sie also das aktuelle API-Dokumentation bevor Sie annehmen, dass derselbe Aufruf gilt.

Zusammenfassung

Rufen Sie ein cache-busted Paar ab, lösen Sie sofort und senden Sie dann challenge, validate und seccode zusammen. Verwenden Sie die zurückgegebene challenge, cachen Sie nur den gt, und rufen Sie innerhalb des Workers ab, statt eine Challenge durch eine Warteschlange zu leiten.

Andere Sprachen finden Sie auf der GeeTest-Löser Seite, die breitere PHP-Oberfläche auf der PHP-Captcha-Löser Seite, und es gibt ein Live-Puzzle auf unserer GeeTest-v3-Demo. CapSkip übernimmt Captcha-Umgehung lokal, sodass Volumen pro Lösung nichts kostet.