So lösen Sie AWS WAF CAPTCHA in Python (405-Seiten)

Um ein AWS WAF CAPTCHA in Python zu lösen, lesen Sie drei Werte von der Seite, die mit HTTP 405 antwortet, und senden diese an den CapSkip-Solver für AWS WAF mit amazon_waf. Die Antwort tauschen Sie danach gegen ein aws-waf-token-Cookie ein. Sie ist kein Token, den Sie in ein Formular einfügen. Sie ist ein Voucher-Paar, das AWS gegen das Cookie eintauscht, daher hat dieser Ablauf einen Schritt mehr als bei reCAPTCHA. Dieser Leitfaden führt die ganze Schleife mit dem CapSkip-SDK für Python und requests in etwa 70 Zeilen aus. CapSkip läuft auf Ihrem eigenen Windows-PC ohne Gebühr pro Lösung, daher kostet eine Wiederholung nichts.
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.
- Python 3.10 oder neuer und das Paket capskip ab Version 1.4.0. Version 1.3.0 hat keine Methode amazon_waf. Der Code unten lief auf Python 3.11.7 und 3.14.0 mit capskip 1.4.0 und requests 2.34.2. Python 3.10 wurde hier nicht ausprobiert.
- Eine PowerShell-Eingabeaufforderung. Die Befehle verwenden PowerShell-Syntax.
- Eine Seite, die Ihnen gehört oder die Sie automatisieren dürfen, hinter einer Regel mit der AWS-WAF-Aktion CAPTCHA.
- 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 die geschützte Website und ein dritter den Token-Host von AWS. Das SDK war das echte capskip 1.4.0. Ich habe das Skript außerdem einmal gegen die CapSkip-App auf meinem PC laufen lassen, mit der Mock-Website als Ziel. Die App kann eine gefälschte Seite nicht lösen, daher antwortete sie dreimal mit ERROR_CAPTCHA_UNSOLVABLE, und das Skript endete mit seinem RuntimeError, was zeigt, dass dieser Fehler als ApiException in der Schleife ankommt. 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 das Cookie akzeptiert, das dieser Ablauf erzeugt.
Schritt 1: Die Challenge-Seite lesen
Wenn eine Regel mit der Aktion CAPTCHA auf eine Anfrage ohne gültigen Token passt, antwortet AWS WAF mit HTTP 405 und dem Header x-amzn-waf-action mit dem Wert captcha. Laut der AWS-WAF-Dokumentation wird die Puzzle-Seite nur dann mitgeschickt, wenn die Anfrage einen Accept-Header sendet, der text/html verlangt. Ein einfacher requests-Aufruf sendet einen generischen Accept, daher setzt das Skript einen. Ohne ihn kann es sein, dass Sie eine 405 ohne lesbaren Inhalt bekommen.
Diese Seite definiert ein kleines Objekt namens gokuProps mit drei Werten: key, iv und context. Sie lädt außerdem challenge.js und captcha.js von einem awswaf.com-Host. Die Werte iv und context ändern sich mit jeder Challenge-Seite, lesen Sie sie also jedes Mal frisch aus. Eine 202-Antwort, bei der der Header auf challenge steht, ist etwas anderes: Das ist die stille Challenge-Aktion von AWS WAF, die kein Puzzle hat. Die CapSkip-Dokumentation beschreibt nur die 405-Seite und das Widget, daher behandelt dieser Leitfaden sie nicht.
# pip install capskip requests
import re
import requests
PAGE_URL = "https://www.example.com/" # the page that answers HTTP 405
HEADERS = { # AWS WAF sends its puzzle page only when the request accepts HTML
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/130.0 Safari/537.36",
"Accept": "text/html",
}
def read_challenge(session):
resp = session.get(PAGE_URL, timeout=30)
if resp.status_code != 405 or "gokuProps" not in resp.text:
return None # no AWS WAF challenge on this response
block = re.search(r"gokuProps\s*=\s*\{(.*?)\}", resp.text, re.S).group(1)
field = lambda name: re.search(rf'"{name}"\s*:\s*"([^"]+)"', block).group(1)
script = lambda name: re.search(rf'<script[^>]+src="([^"]+/{name}\.js)"', resp.text).group(1)
return {
"sitekey": field("key"),
"iv": field("iv"),
"context": field("context"),
"challenge_script": script("challenge"),
"captcha_script": script("captcha"),
}Die Funktion gibt None zurück, wenn die Antwort keine Captcha-Seite ist, sodass ein Skript “nichts zu lösen” von “gelöst” unterscheiden kann. Sie liest die Werte mit zwei kleinen regulären Ausdrücken statt mit einem JSON-Parser, weil die Seite JavaScript enthält, kein JSON. Wenn Ihr Ziel die Schlüssel anders in Anführungszeichen setzt, passen Sie den Ausdruck an und sonst nichts.
Schritt 2: Mit amazon_waf lösen
Die SDK-Methode heißt amazon_waf. Nur sitekey und url sind Pflicht. Bei einer Challenge-Seite ist sitekey gleich gokuProps.key, und iv und context sind die beiden anderen Werte. Sie gehören zusammen: Senden Sie beide oder keinen. Lassen Sie beide weg, liest CapSkip sie zum Lösezeitpunkt aus der Seite, was die Dokumentation die sicherere Wahl nennt, wenn Ihre Kopie der Seite mehr als ein paar Minuten alt ist. Dieser Leitfaden sendet sie, weil er die Seite gerade eben abgerufen hat.
Die beiden Skript-Adressen sind Optionen. Jede muss eine https-Adresse auf einem awswaf.com-Host sein, und das SDK weist alles andere ab, bevor eine Anfrage gesendet wird. Die Antwort kommt als Dictionary mit captcha_voucher und existing_token zurück, 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.
from capskip import CapSkip
# Local mode. In Server mode, use your server's address instead.
solver = CapSkip(host="127.0.0.1", port=8080)
def solve(challenge):
# The answer is a voucher pair, not a token.
return solver.amazon_waf(
sitekey=challenge["sitekey"],
iv=challenge["iv"],
context=challenge["context"],
url=PAGE_URL,
challenge_script=challenge["challenge_script"],
captcha_script=challenge["captcha_script"],
)Um das Lösen über einen Proxy zu leiten, fügen Sie dem Aufruf ein Argument proxy hinzu: ein Dictionary 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. Websites hinter AWS WAF koppeln das Captcha oft mit Rate Limits, daher wird ein stetiger Strom von Lösungen von einer Adresse strenger behandelt, und die CapSkip-Dokumentation empfiehlt eine Proxy-Liste unter Settings 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. Der Leitfaden zur Proxy-Rotation zeigt, wie Sie Proxys rotieren.
Schritt 3: Den Voucher gegen das aws-waf-token-Cookie tauschen
Das Voucher-Paar ist allein nutzlos. AWS tauscht es gegen das fertige Cookie an einem Voucher-Endpunkt, der neben challenge.js liegt: im selben Ordner, mit voucher an der Stelle von challenge.js. Senden Sie das Paar per POST als JSON, und die Antwort hat ein Feld token. Die CapSkip-Dokumentation sendet den Body als text/plain mit einem Origin-Header, der zur Website passt. Der eigene Code der CapSkip-App sendet außerdem einen Referer, daher sendet die Funktion beides. Die SDK-Dokumentation sagt, dass das Paar kurzlebig ist, tauschen Sie es also in dem Moment ein, in dem es ankommt, und speichern Sie es nie.
import json
from urllib.parse import urlsplit
def exchange(result, challenge):
# The voucher endpoint sits beside challenge.js, same folder.
voucher_url = challenge["challenge_script"].rsplit("/", 1)[0] + "/voucher"
site = urlsplit(PAGE_URL)
pair = {
"captcha_voucher": result["captcha_voucher"],
"existing_token": result["existing_token"],
}
reply = requests.post(
voucher_url,
data=json.dumps(pair),
headers={
"Content-Type": "text/plain;charset=UTF-8",
"Origin": f"{site.scheme}://{site.netloc}",
"Referer": f"{site.scheme}://{site.netloc}/",
},
timeout=30,
)
reply.raise_for_status()
return reply.json()["token"] # the aws-waf-token cookie valueSchritt 4: Das vollständige funktionierende Beispiel
Der letzte Block fügt den Ablauf für das AWS WAF CAPTCHA in Python zusammen und ergänzt die Wiederholung. Speichern Sie alle vier Blöcke nacheinander in einer Datei und führen Sie sie aus. Jeder Versuch ruft die Seite erneut ab, daher trägt jeder Versuch frische Werte für iv und context. Ein fehlgeschlagenes Puzzle löst ApiException aus, und die Schleife versucht es mit einem neuen Puzzle erneut. Nachdem der Token angekommen ist, fordert das Skript die Seite mit gesetztem Cookie an.
from capskip import ApiException
def fetch_protected():
session = requests.Session()
session.headers.update(HEADERS)
for attempt in range(1, 4):
challenge = read_challenge(session) # fresh page, fresh iv and context
if challenge is None:
return session.get(PAGE_URL, timeout=30) # no challenge, real page
try:
result = solve(challenge)
except ApiException as err:
print(f"attempt {attempt}: {err}") # each retry draws a new puzzle
continue
token = exchange(result, challenge) # exchange at once, never store it
return session.get(PAGE_URL, cookies={"aws-waf-token": token}, timeout=30)
raise RuntimeError("AWS WAF CAPTCHA not solved in 3 attempts")
if __name__ == "__main__":
page = fetch_protected()
print(page.status_code, page.text[:60])Im Mock-Lauf verhielt sich das Skript auf beiden Python-Versionen so:
| Mock-Einstellung | Was das Skript tat |
|---|---|
| Jedes Puzzle beantwortet | Eine Aufgabe, ein Voucher-Tausch, dann Status 200 mit der echten Seite |
| Die ersten zwei Puzzles abgelehnt | Zwei Versuchszeilen, dann Status 200 bei der dritten Aufgabe |
| Die ersten drei Puzzles abgelehnt | Drei Versuchszeilen, dann RuntimeError |
| Anfrage ohne Accept-Header | Der Mock sendete eine 405 ohne Seite, read_challenge gab None zurück und das Skript gab 405 aus, behalten Sie den Header also bei |
Das Cookie 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. Kommt die 405 wieder zurück, führen Sie die Schleife erneut aus. Wenn Sie viele Anfragen senden, behalten Sie den Token und verwenden Sie ihn bis dahin wieder, statt einmal pro Anfrage zu lösen.
Häufige Fehler
| Fehler | Ursache | Beheben |
|---|---|---|
| ValidationException | iv ohne context, eine Skript-Adresse, die nicht https auf einem awswaf.com-Host ist, oder eine url, die keine vollständige Adresse ist | Senden Sie iv und context zusammen und kopieren Sie jede Skript-Adresse genau so, wie die Seite sie hat |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE | Das Puzzle konnte nicht beantwortet werden | Senden Sie erneut. Die API-Dokumentation weist darauf hin, dass jeder Versuch ein neues Puzzle bekommt |
| ERROR_TASK_NOT_SUPPORTED | Die API-Dokumentation führt dies für einen Aufgabentyp auf, der in CapSkip ausgeschaltet ist | Prüfen Sie die AWS-WAF-Einstellungen in der CapSkip-App |
| ERROR_PROXY_FORMAT | Der Proxy-String konnte nicht geparst werden | Verwenden Sie user:pass@ip:port |
| ERROR_NO_SLOT_AVAILABLE | Der API-Schlüssel hat ein Limit für die Anfragerate, und Sie haben es überschritten | Warten Sie ein paar Sekunden und senden Sie erneut |
| NetworkException | CapSkip läuft nicht, oder Host oder Port sind falsch | Starten Sie CapSkip und prüfen Sie die Verbindungseinstellungen |
| Der Voucher-POST wird abgelehnt | Am wahrscheinlichsten ein veraltetes oder nicht passendes Paar. 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 reproduzieren | Tauschen Sie sofort ein und senden Sie eine neue Aufgabe |
| Weiterhin eine 405 trotz Cookie | Der Token ist abgelaufen, oder die Website liest ihn von woanders | Lösen Sie erneut und prüfen Sie, wie die eigenen Anfragen der Seite den Token übertragen |
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
Kann ich ein AWS WAF CAPTCHA in Python 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. Der Status 405 ist einfach die Art, wie AWS WAF seine Aktion CAPTCHA kennzeichnet.
Gibt das SDK den Wert aws-waf-token direkt zurück?
Nein. Es gibt captcha_voucher und existing_token zurück, und Sie tauschen sie am Voucher-Endpunkt gegen den Token ein. Die JSON-API createTask akzeptiert ebenfalls AWS WAF, und mehrere ihrer Formate liefern den Token direkt. Die API-Dokumentation führt sie auf.
Was ist, wenn das Captcha ein Widget in einem Formular ist?
Das ist die zweite Variante, die AWS WAF verwendet. Die Werte stammen statt aus gokuProps aus dem apiKey des Widgets und seiner jsapi.js-Adresse. Der Leitfaden zum Node.js-Widget geht es der Reihe nach durch, und der Aufruf nutzt dieselbe Methode.
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 Python besteht aus vier Schritten: die 405-Seite mit einem HTML-Accept-Header lesen, amazon_waf aufrufen, das Voucher-Paar gegen das Cookie tauschen und die Anfrage damit wiederholen. Eine Wiederholung ist auf Ihrem eigenen Rechner kostenlos, und genau dafür ist ein unbegrenzter Captcha-Löser da, wenn eine geschützte Website Ihnen manchmal ein Puzzle gibt, das sich nicht beantworten lässt. Für den Rest des SDK siehe die Python-Captcha-Solver-Seite.
