So lösen Sie ein AWS-WAF-Widget-CAPTCHA mit Node.js

aws waf captcha in node.js - How to Solve an AWS WAF Widget CAPTCHA With Node.js

Um ein AWS WAF CAPTCHA in Node.js auf einer Widget-Seite zu lösen, lesen Sie den apiKey des Widgets und die jsapi.js-Adresse von der Seite, senden beides an den CapSkip-Solver für AWS WAF mit amazonWaf, tauschen das zurückgegebene Voucher-Paar gegen einen Token und senden diesen Token mit Ihrer nächsten Anfrage. Das Widget ist die zweite Variante, die AWS WAF verwendet. Eine Formular- oder Login-Seite zeichnet das Puzzle selbst, daher gibt es keine HTTP-405-Seite und kein gokuProps zum Lesen. Dieser Leitfaden führt die ganze Schleife mit dem CapSkip-SDK für Node.js und dem fetch aus, das mit Node.js mitgeliefert wird. Die 405-Seite hat ihren eigenen Python-Leitfaden.

Was Sie brauchen

  • CapSkip 3.0.0 oder neuer, das auf einem Windows-PC läuft. Mit dieser Version kam die Unterstützung für das Lösen von AWS WAF dazu. Die Einrichtungsanleitung führt durch den ersten Start.
  • Node.js 18 oder neuer, was das Paket verlangt, und das Paket capskip ab Version 1.4.0. Frühere Versionen haben keine Methode amazonWaf. Der Code unten lief auf Node.js 24.22.0 mit capskip 1.4.0. Keine andere Node.js-Version wurde hier ausprobiert.
  • Eine PowerShell-Eingabeaufforderung. Die Befehle verwenden PowerShell-Syntax.
  • Eine Seite, die Ihnen gehört oder die Sie automatisieren dürfen, mit einem AWS-WAF-CAPTCHA-Widget darauf, und ein Browser mit Entwicklertools, um sie sich einmal anzusehen.
  • Eine Adresse für den Solver. Im Local-Modus antwortet CapSkip auf 127.0.0.1 nur für dieses Gerät. Im Server-Modus lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, damit ein Skript auf einem anderen PC, einem VPS oder einer gehosteten Plattform es über die API aufrufen kann. Eingestellt werden beide Modi in den Verbindungseinstellungen.

So wurde getestet: Der Code lief gegen drei Mock-Server, die ich geschrieben habe, nicht gegen eine echte AWS-WAF-Website. Einer bildet die AWS-WAF-Prüfungen nach, die in der CapSkip API-Dokumentation aufgeführt sind, ein zweiter spielt eine Website mit einem Widget auf ihrer Kontaktseite und ein dritter den Token-Host von AWS. Das SDK war das echte capskip 1.4.0. Dieser Code lief nicht gegen die echte CapSkip-App, und es war keine Live-Website mit AWS WAF beteiligt, daher ist keine der folgenden Zahlen eine Lösungszeit, und ich kann Ihnen nicht sagen, ob eine bestimmte Live-Website den Token akzeptiert, den dieser Ablauf erzeugt.

Schritt 1: Das Projekt einrichten und die Widget-Werte finden

Legen Sie einen Ordner an, initialisieren Sie ein Projekt und installieren Sie capskip:

npm init -y
npm install capskip

Eine Widget-Seite lädt ein Skript namens jsapi.js und ruft AwsWafCaptcha.renderCaptcha mit einem Options-Objekt auf. Laut der AWS-WAF-Dokumentation ist der apiKey in diesem Objekt ein verschlüsselter Schlüssel, der die Client-Domains auflistet, die das Widget verwenden dürfen, daher gehört er zu genau einer Website. CapSkip braucht von der Seite zwei Dinge: diesen apiKey, den es sitekey nennt, und die vollständige Adresse von jsapi.js. Eine Widget-Seite hat kein gokuProps, daher senden Sie weder iv noch context. Dass Sie die jsapi.js-Adresse ohne sie senden, kennzeichnet die Anfrage als Widget-Lösung.

Der erste Block liest beide aus dem HTML der Seite. Manche Websites halten den apiKey stattdessen in einem separaten JavaScript-Bundle. In diesem Fall gibt die Funktion null zurück, und die Lösung ist, in den Skripten, die die Seite lädt, nach apiKey zu suchen.

'use strict';
const { CapSkip, ApiException } = require('capskip');

const PAGE_URL = 'https://www.example.com/contact'; // the page that draws the widget
// The folder that the page's challenge.js loads from (see the Network tab).
const TOKEN_BASE = 'https://3f2a9c7d41b0.edge.sdk.awswaf.com/3f2a9c7d41b0/8e51d0c7a2f4/';

async function readWidget() {
  const res = await fetch(PAGE_URL, { headers: { Accept: 'text/html' } });
  const html = await res.text();
  const src = /<script[^>]+src="([^"]*\/jsapi\.js)"/i.exec(html);
  const key = /apiKey\s*:\s*["']([^"']+)["']/.exec(html);
  if (!src || !key) return null; // not in the HTML: look in the page's scripts
  return { sitekey: key[1], jsapiScript: new URL(src[1], PAGE_URL).href };
}

Die Skript-Adresse auf einer Seite kann relativ sein oder mit zwei Schrägstrichen beginnen, daher macht die Funktion mit der Seiten-URL eine vollständige Adresse daraus. TOKEN_BASE oben im Block ist eine Konstante, die Sie von Hand eintragen, und der nächste Schritt erklärt, warum.

Schritt 2: Den Token-Host einmal finden

Das Voucher-Paar wird auf einem eigenen Host von AWS gegen einen Token getauscht. Auf einer Widget-Seite steht dieser Host nicht in dem HTML, das Sie abgerufen haben. Öffnen Sie die Seite in einem Browser, wechseln Sie zum Tab Network, laden Sie neu und suchen Sie die Anfrage an challenge.js, die jsapi.js für Sie stellt. Zu kopieren ist deren Ordner, mit einem Schrägstrich am Ende. Die CapSkip-Dokumentation sagt, challenge.js durch voucher zu ersetzen, und genau das tut der Code im nächsten Schritt mit TOKEN_BASE.

Das ist ein einmaliges Nachschlagen pro Website, halten Sie es daher in Ihrer Konfiguration fest. Ich kann nicht sagen, wie lange es auf einer echten Website gleich bleibt. Beginnt der Tausch im nächsten Schritt zu scheitern, prüfen Sie den Tab Network erneut, bevor Sie etwas anderes verdächtigen.

Schritt 3: Lösen und den Voucher tauschen

Die SDK-Methode heißt amazonWaf. Nur der sitekey und die Seiten-URL sind Pflicht, und die Optionen tragen die jsapi.js-Adresse. Die Skript-Adresse muss eine https-Adresse auf einem awswaf.com-Host sein, und das SDK weist alles andere ab, bevor eine Anfrage gesendet wird. Die Antwort ist ein Objekt mit captcha_voucher und existing_token, dazu die Rohantwort unter code und die Aufgabennummer unter captchaId. Die Methode wartet mit dem längeren Timeout von 300 Sekunden, den auch reCAPTCHA verwendet, weil CapSkip das Puzzle in einem echten Browser löst. Die Zeit habe ich nicht gemessen.

Der Tausch ist ein POST an den Voucher-Endpunkt mit dem Paar als JSON-Body. Die CapSkip-Dokumentation sendet ihn als text/plain mit einem Origin-Header, der zur Website passt. Der eigene Code der CapSkip-App sendet außerdem einen Referer, daher sendet der Code beides. Mit dem fetch von Node.js können Sie Origin setzen, was ein Browser nicht zulassen würde. Die SDK-Dokumentation sagt, dass das Paar kurzlebig ist, tauschen Sie es also sofort und speichern Sie es nie.

// Local mode. In Server mode, use your server's address instead.
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });

async function getToken(widget) {
  // The answer is a voucher pair, not a token.
  const result = await solver.amazonWaf(widget.sitekey, PAGE_URL, {
    jsapiScript: widget.jsapiScript,
  });
  const reply = await fetch(new URL('voucher', TOKEN_BASE), {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain;charset=UTF-8',
      Origin: new URL(PAGE_URL).origin,
      Referer: new URL(PAGE_URL).origin + '/',
    },
    body: JSON.stringify({
      captcha_voucher: result.captcha_voucher,
      existing_token: result.existing_token,
    }),
  });
  if (!reply.ok) throw new Error(`voucher exchange failed: HTTP ${reply.status}`);
  return (await reply.json()).token; // the aws-waf-token value
}

Um das Lösen über einen Proxy zu senden, fügen Sie dem Aufruf eine Option proxy hinzu: ein Objekt mit type auf HTTP und uri auf user:[email protected]:8080 gesetzt. Im Mock-Lauf kam das bei in.php als proxy und proxytype an. Eine Adresse reicht zum Testen, aber Websites hinter AWS WAF koppeln das Captcha oft mit Rate Limits, daher wird ein stetiger Strom von Lösungen von einer Adresse strenger behandelt. Die API-Dokumentation empfiehlt eine Proxy-Liste in den CapSkip-Einstellungen oder einen Proxy pro Aufruf, wenn der Token aus einem bestimmten Netzwerk stammen muss. Der eigene Solver von CapSkip schickt seine Seitenaufrufe, das Puzzle und den Voucher-Tausch über einen Proxy, sodass AWS eine Adresse sieht. Wenn Sie dem Lösen einen Proxy übergeben, schicken Sie Ihre eigenen Anfragen über denselben. Diesen Teil konnte ich nicht testen.

Schritt 4: Den Token so senden, wie es die Seite tut

Auf einer Widget-Seite ist der Token derselbe, den der eigene onSuccess-Callback der Seite erhält. Seiten unterscheiden sich darin, wie sie ihn weitergeben. Viele senden ihn als Cookie aws-waf-token und andere als Header x-aws-waf-token, senden Sie das Formular also einmal von Hand ab, mit geöffnetem Tab Network, und kopieren Sie, was die Anfrage der Seite trägt. Die Funktion unten sendet das Cookie und zeigt den Header als Alternative. Die Formularfelder sind Platzhalter für Ihre.

function submitForm(token) {
  return fetch(PAGE_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Cookie: `aws-waf-token=${token}`, // or 'x-aws-waf-token': token
    },
    body: new URLSearchParams({ email: '[email protected]', message: 'Hello' }),
  });
}

Schritt 5: Das vollständige funktionierende Beispiel

Der letzte Block macht aus den Teilen einen vollständigen Solver für AWS WAF CAPTCHA in Node.js, mit der Wiederholung und dem Einstiegspunkt. Speichern Sie alle vier Codeblöcke nacheinander in einer Datei und führen Sie sie mit node aus. Ein fehlgeschlagenes Puzzle löst ApiException aus, und die Schleife versucht es erneut, wobei jedes Mal ein neues Puzzle kommt. Jeder andere Fehler wird geworfen, sodass ein falscher Host oder ein abgelehnter Tausch nicht stillschweigend wiederholt wird.

async function main() {
  const widget = await readWidget();
  if (!widget) throw new Error('no jsapi.js script or apiKey in the page HTML');
  for (let attempt = 1; attempt <= 3; attempt++) {
    try {
      const token = await getToken(widget); // exchange at once, never store it
      const res = await submitForm(token);
      console.log(res.status, await res.text());
      return;
    } catch (err) {
      if (!(err instanceof ApiException)) throw err;
      console.log(`attempt ${attempt}: ${err.message}`); // each retry draws a new puzzle
    }
  }
  throw new Error('AWS WAF CAPTCHA not solved in 3 attempts');
}

main().catch((err) => {
  console.error(`${err.constructor.name}: ${err.message}`);
  process.exitCode = 1;
});

Im Mock-Lauf verhielt sich das Skript so:

Mock-EinstellungWas das Skript tat
Jedes Puzzle beantwortetEine Aufgabe, ein Voucher-Tausch, dann Status 200 und der Text Message sent
Die ersten zwei Puzzles abgelehntZwei Versuchszeilen, dann Status 200 bei der dritten Aufgabe
Die ersten drei Puzzles abgelehntDrei Versuchszeilen, dann die Fehlerzeile und Exit-Code 1

Der Token funktioniert so lange wie die Immunitätszeit der Website. AWS setzt standardmäßig 300 Sekunden und lässt den Betreiber der Website einen Wert von 60 Sekunden bis zu drei Tagen wählen, wie die AWS-WAF-Dokumentation erklärt. Wenn Sie viele Anfragen senden, behalten Sie den Token und verwenden Sie ihn wieder, bis er abläuft, statt einmal pro Anfrage zu lösen.

Häufige Fehler

FehlerUrsacheBeheben
kein jsapi.js-Skript und kein apiKey im HTML der SeiteDie Seite baut das Widget aus einem Bundle, daher enthält das HTML sie nichtDurchsuchen Sie die geladenen Skripte nach apiKey und jsapi.js und übergeben Sie die Werte dann von Hand
ValidationExceptioniv ohne context, oder eine Skript-Adresse, die keine vollständige https-Adresse auf einem awswaf.com-Host istSenden Sie für ein Widget weder iv noch context und kopieren Sie die jsapi.js-Adresse genau so, wie die Seite sie hat
ApiException mit ERROR_CAPTCHA_UNSOLVABLEDas Puzzle konnte nicht beantwortet werdenSenden Sie erneut. Die API-Dokumentation weist darauf hin, dass jeder Versuch ein neues Puzzle bekommt
ERROR_TASK_NOT_SUPPORTEDDie API-Dokumentation führt dies für einen Aufgabentyp auf, der in CapSkip ausgeschaltet istPrüfen Sie die AWS-WAF-Einstellungen in der CapSkip-App
ERROR_PROXY_FORMATDer Proxy-String konnte nicht geparst werdenVerwenden Sie user:pass@ip:port
ERROR_NO_SLOT_AVAILABLEDer API-Schlüssel hat ein Limit für die Anfragerate, und Sie haben es überschrittenWarten Sie ein paar Sekunden und senden Sie erneut
NetworkExceptionCapSkip läuft nicht, oder Host oder Port sind falschStarten Sie CapSkip und prüfen Sie die Verbindungseinstellungen
voucher exchange failed: HTTP 4xxAm wahrscheinlichsten ein veraltetes oder nicht passendes Paar oder das falsche TOKEN_BASE. Der Code der CapSkip-App erwartet von AWS HTTP 400 bei einem ungültigen existing_token. Diesen Fall konnte ich nicht gegen das echte AWS reproduzierenTauschen Sie sofort ein, prüfen Sie den Ordner im Tab Network und senden Sie eine neue Aufgabe
Der Formular-POST wird weiterhin abgelehntDer Token ist abgelaufen, oder die Seite übergibt ihn in einem Header und Sie haben ein Cookie gesendetKopieren Sie, was die eigene Anfrage der Seite trägt

Den Löser auf einem Server betreiben

Das Skript braucht von CapSkip zwei Dinge: einen Host und einen Port. Im Local-Modus sind das 127.0.0.1 und Ihr eigener PC. Im Server-Modus lauscht CapSkip auf Ihrer Netzwerkadresse oder einer öffentlichen IP, sodass ein Skript auf einem anderen PC, einem VPS oder einer gehosteten Plattform es über dieselbe API aufrufen kann. Ändern Sie Host und Port im Konstruktor CapSkip, und nichts anderes im Code ändert sich. Eine statische öffentliche IP wird für stabilen Fernzugriff empfohlen. Der Server-Modus bleibt Ihre eigene Hardware, und es fallen weiterhin keine Gebühren pro Lösung an. Er öffnet den Port allerdings für jede Adresse, fügen Sie daher eine Blockierregel der Windows Firewall für die Adressen hinzu, die ihn nicht erreichen sollen. Die Verbindungseinstellungen zeigen, wie Sie den Modus wechseln.

FAQ

Wie unterscheide ich eine Widget-Seite von einer 405-Challenge-Seite?

Eine Challenge-Seite beantwortet die Anfrage mit HTTP 405 und definiert gokuProps. Eine Widget-Seite ist eine gewöhnliche Seite, die jsapi.js lädt und renderCaptcha aufruft, meist an einem Formular. Dieselbe Methode amazonWaf bearbeitet beide, mit unterschiedlichen Werten.

Kann ich ein AWS WAF CAPTCHA in Node.js ohne Browser lösen?

Ihr Skript braucht keinen Browser. Es sendet einfache HTTP-Anfragen an CapSkip und an die Website, und CapSkip löst das Puzzle in einem echten Browser auf dem Rechner, auf dem es läuft, wie die SDK-Dokumentation beschreibt. Sie öffnen nur einmal selbst einen Browser, um die Werte von der Seite abzulesen.

Gibt das SDK den Token direkt zurück?

Nein. Es gibt captcha_voucher und existing_token zurück, und Sie tauschen sie am Voucher-Endpunkt ein. Die JSON-API createTask akzeptiert ebenfalls AWS WAF, und mehrere ihrer Formate liefern den Token direkt. Die API-Dokumentation führt sie auf.

Kann eine gehostete Plattform das gegen mein CapSkip ausführen?

Ja, über den Server-Modus. Stellen Sie CapSkip so ein, dass es auf Ihrer Netzwerkadresse oder öffentlichen IP lauscht, richten Sie den CapSkip-Konstruktor auf diesen Host und Port, und die Schleife funktioniert genauso wie auf einem PC. Der Solver bleibt auf Ihrer Hardware.

Die Kurzfassung

Ein AWS WAF CAPTCHA in Node.js ist dasselbe Puzzle auf einem Widget wie auf einer 405-Seite, nur an einer anderen Stelle: den apiKey und die jsapi.js-Adresse lesen, amazonWaf aufrufen, das Paar am Voucher-Endpunkt tauschen und den Token so senden, wie es die Seite tut. CapSkip ist eine Captcha-Umgehung auf Ihrem eigenen Rechner, daher kostet ein Puzzle, das es nicht beantworten kann, eine Wiederholung und keine Gebühr. Für den Rest des SDK siehe die Node.js-Captcha-Solver-Seite.