So lösen Sie Friendly Captcha in C# (v1- und v2-Widgets)

Um Friendly Captcha in C# zu lösen, rufen Sie FriendlyCaptchaAsync mit dem sitekey, der Seiten-URL und der Widget-Version auf und setzen den Token dann in das Formularfeld ein, das diese Version erwartet. Die Version ist dabei alles. Friendly Captcha liefert unter einem Namen zwei verschiedene Protokolle aus, und beide teilen sich einen gemeinsamen sitekey-Namensraum, sodass nichts im Schlüssel verrät, welches davon eine Website verwendet. Lösen Sie das falsche, erhalten Sie einen formal korrekten Token, den die Website ablehnt, ohne zu sagen, warum. CapSkip hat Friendly Captcha in Version 1.4.0 hinzugefügt. Dieser Leitfaden zeigt, wie Sie die beiden unterscheiden, wie der Aufruf aussieht und wie Sie den Token absenden.
Was Sie brauchen
- CapSkip 1.4.0 oder neuer auf einem Windows-Rechner. Die Unterstützung für Friendly Captcha kam mit diesem Release, zusammen mit CaptchaFox und Capy Puzzle.
- Version 1.3.0 oder neuer des CapSkip .NET-Pakets, also das Release, das FriendlyCaptchaAsync hinzugefügt hat. Es zielt auf .NET Standard 2.0, daher funktionieren .NET Framework 4.6.1 und höher, .NET Core 2.0 und höher sowie .NET 6 und neuer. Die Beispiele verwenden Top-Level-Anweisungen, führen Sie sie also unter .NET 6 oder neuer aus.
- Drei Werte von der Zielseite: der sitekey am Widget-Element, die URL der Seite selbst und die Adresse des Widget-Skripts, das die Seite lädt. Schritt 1 zeigt, wo Sie jeden davon finden.
- Eine Adresse für den Solver. Der Local-Modus antwortet auf 127.0.0.1 nur für dieses Gerät; der Server-Modus lauscht auf Ihrer Netzwerkadresse oder öffentlichen IP, damit ein anderer Rechner ihn über die API aufrufen kann. Beide finden Sie unter Verbindungseinstellungen, und Schritt 4 erklärt, welchen davon Sie brauchen.
# dotnet add package CapSkip dotnet add package CapSkip
Schritt 1: Friendly Captcha v1 von v2 unterscheiden
Beide Versionen rendern dasselbe Element, ein div mit der Klasse frc-captcha und einem Attribut data-sitekey. Das Element verrät Ihnen also, wo der sitekey steht, aber nichts über das Protokoll. Verräterisch ist erst das Script-Tag, das das Widget lädt, denn v1 und v2 sind verschiedene Pakete mit unterschiedlichen Dateinamen.
| Was Sie vergleichen | Version 1 | Version 2 |
|---|---|---|
| Paket, das die Seite lädt | friendly-challenge | @friendlycaptcha/sdk |
| Skriptdatei | widget.module.min.js, mit widget.min.js als Fallback | site.min.js, mit site.compat.min.js als Fallback |
| Formularfeld, in das der Token gehört | frc-captcha-solution | frc-captcha-response |
| Wie ein Token aussieht | Vier durch Punkte getrennte Teile, einige hundert Zeichen lang | Eine opake Zeichenfolge, die mit AQQA und einem Punkt beginnt, rund sechs Kilobyte groß |
Öffnen Sie den Seitenquelltext, suchen Sie nach frc-captcha, um das Element zu finden, und lesen Sie dann die Script-Tags, die es laden. Friendly Captcha dokumentiert dieselbe Prüfung auf seiner eigenen Seite zu den beiden Versionen. Wenn die Website das Widget selbst hostet, kann der Paketname aus der URL verschwinden, richten Sie sich dann stattdessen nach dem Dateinamen. Schließen Sie nicht vom Alter der Website auf die Version: Beide Versionen sind im Einsatz, und Friendly Captcha gibt an, v1 noch mehrere Jahre weiter zu pflegen.
Schritt 2: Der Aufruf von FriendlyCaptchaAsync
Eine Methode, drei Argumente: der sitekey, die Seiten-URL und ein Options-Dictionary. Übergeben Sie die Version, die Sie in Schritt 1 ermittelt haben.
// dotnet add package CapSkip
using CapSkip;
var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);
// Tell the solver which protocol the site runs.
var result = await solver.FriendlyCaptchaAsync(
"YOUR_SITEKEY",
"https://example.com/signup",
new Dictionary<string, object?> { ["version"] = "v2" });
Console.WriteLine(result.Token); // goes in frc-captcha-responseDie Option version nimmt v1 oder v2 an, eine bloße 1 oder 2 funktioniert ebenfalls. Jeden anderen Wert lehnt das SDK mit einer ValidationException ab, bevor eine Anfrage gestellt wird. Das ist Absicht: Ein geratener Wert käme als gültig aussehender Token zurück, den die Website verwirft, und das ist schlimmer als ein Fehler. Eine Version mit dem Wert null wird allerdings nicht abgelehnt. Sie wird weggelassen, und der Solver fällt auf v1 zurück, wie unten beschrieben.
Lesen Sie die Eigenschaft Token. Die Eigenschaft Code enthält dieselbe Zeichenfolge, aber Token ist nach dem Feld benannt, in das es gehört. Der User-Agent bleibt hier null, da nur Turnstile und CaptchaFox einen melden.
Die Skriptadresse über die Version entscheiden lassen
Wenn Ihr Code die Adresse des Widget-Skripts bereits hat, übergeben Sie stattdessen diese, und CapSkip liest die Version aus dem Build ab, den die Website tatsächlich lädt. Das ist das zuverlässigste Signal, das es gibt. Der Solver prüft in einer festen Reihenfolge und hört bei der ersten Antwort auf: zuerst die Option version, dann die Skriptadresse, dann der Standardwert v1.
// Instead of the call above: copy the src straight off the page's module script tag.
var result = await solver.FriendlyCaptchaAsync(
"YOUR_SITEKEY",
"https://example.com/signup",
new Dictionary<string, object?>
{
["module_script"] =
"https://cdn.jsdelivr.net/npm/@friendlycaptcha/[email protected]/site.min.js",
});Die Option nomodule_script erledigt dasselbe mit dem Fallback-Skript, falls Sie dieses haben. Achten Sie aber auf den letzten Schritt dieser Reihenfolge. Ohne Version und ohne Skriptadresse nimmt der Solver v1 an, eine v2-Website, die nur mit dem sitekey gelöst wird, scheitert also genau auf die Art, vor der dieser ganze Leitfaden warnt.
Websites am EU-Endpunkt
Friendly Captcha verkauft eine Option zur Datenresidenz, bei der die Rätsel ausschließlich aus Deutschland ausgeliefert werden. Ein v2-Widget auf diesem Dienst trägt ein Attribut data-api-endpoint, meist mit dem Wert eu, und wenn Sie es sehen, übergeben Sie denselben Wert als Option api_server. Diese Option nimmt global (den Standard), eu oder eine vollständige URL an. Beide Endpunkte stellen für denselben sitekey einen Token aus, ein Fehler an dieser Stelle fällt also erst bei der Verifizierung durch die Website selbst auf, und das ist wieder die stille Art des Scheiterns.
Schritt 3: Den Token in das richtige Feld senden
Der Feldname ist das Zweite, woran viele scheitern, meist direkt nachdem eine funktionierende v1-Integration auf eine v2-Website gerichtet wurde. Das Widget schreibt seinen Token in ein verstecktes Eingabefeld, und Ihre Anfrage muss ihn an dieselbe Stelle setzen.
// version holds what you passed in Step 2; http is your HttpClient.
// v1 reads frc-captcha-solution, v2 reads frc-captcha-response.
var isV2 = version is "v2" or "V2" or "2";
var field = isV2 ? "frc-captcha-response" : "frc-captcha-solution";
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
[field] = result.Token!,
});
var response = await http.PostAsync("https://example.com/signup", body);Dazu kommen zwei Details. Eine Website kann das Feld über ein Attribut am Widget-Element umbenennen, data-form-field-name bei v2 und data-solution-field-name bei v1, prüfen Sie also, ob es vorhanden ist, und verwenden Sie gegebenenfalls seinen Wert. Und manche Integrationen senden den Token in einem JSON-Body statt als Formular-POST, öffnen Sie also die DevTools, senden Sie das Formular einmal von Hand ab und bilden Sie genau nach, was die Seite sendet.
Bei v2 spielt die Größe eine Rolle. Ein Token von etwa sechs Kilobyte ist in einem POST-Body kein Problem, kann aber das Query-String-Limit eines Servers überschreiten oder von einer zu schmalen Datenbankspalte abgeschnitten werden, und ein gekürzter Token scheitert bei der Verifizierung genauso wie ein falscher. Halten Sie ihn vollständig und senden Sie ihn nur einmal. Die Verifizierung von Friendly Captcha lehnt in beiden Versionen eine Antwort ab, die abgelaufen ist oder schon verwendet wurde, ein Token reicht also für genau ein Absenden und sollte zügig verschickt werden.
Schritt 4: Timeouts und wo der Solver läuft
Friendly Captcha ist Proof of Work, aber nicht von der Millisekunden-Sorte. Der Dienst entscheidet im Moment jeder Anfrage, wie viel Arbeit sie wert ist, und erhöht diesen Wert für Adressen, die er schon oft gesehen hat. CapSkip löst außerdem jedes v2-Widget in einem echten Browser. Deshalb läuft die Methode auf dem längeren Polling-Timeout für reCAPTCHA und nicht auf dem Standard-Timeout.
| Konstruktor-Option | Standard | Was sie abdeckt |
|---|---|---|
| recaptchaTimeout | 300 Sekunden | Polling für Friendly Captcha, CaptchaFox, reCAPTCHA und GeeTest |
| defaultTimeout | 120 Sekunden | Polling für Bild-Captchas, ALTCHA und Capy |
| pollingInterval | Maximal 5 Sekunden | Das Polling beginnt bei 0,25 Sekunden und steigt per Backoff bis auf diesen Wert |
Wenn das Lösen über einen langen Durchlauf hinweg langsamer wird, ist diese steigende Schwierigkeit der wahrscheinliche Grund, und mehr Adressen sind die Abhilfe. Die Methode akzeptiert einen Proxy pro Anfrage, und ein in CapSkip konfigurierter Proxy-Pool verteilt die Last für Sie. Beides lohnt sich hier früher als bei den meisten anderen Typen.
Die Beispiele verwenden 127.0.0.1, weil das richtig ist, wenn Ihr Code und der Solver auf demselben Rechner liegen. Sobald der aufrufende Code irgendwo anders läuft, etwa in einem Container, auf einem Build-Agent oder einem VPS, zeigt Loopback auf den falschen Rechner, und der erste Lösungsversuch wirft eine NetworkException. Schalten Sie CapSkip in den Server-Modus, dann lauscht er stattdessen auf Ihrer Netzwerkadresse oder öffentlichen IP, sodass jede dieser Umgebungen ihn über die API erreichen kann. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, zusammen mit einer Firewall-Regel für die Adressen, die Sie erwarten. Es bleibt Ihre Hardware, und es bleibt ohne Abrechnung pro Lösung.
Der Client liest Umgebungsvariablen nicht von selbst. Lesen Sie CAPSKIP_HOST in Ihrem eigenen Code aus und übergeben Sie den Wert an den Konstruktor, wie es das vollständige Beispiel tut, damit ein Build sowohl auf Ihrem Schreibtisch als auch auf einem Server funktioniert.
Vollständiges lauffähiges Beispiel
// dotnet add package CapSkip
using System.Text.RegularExpressions;
using CapSkip;
var pageUrl = "https://example.com/signup";
var http = new HttpClient();
var solver = new CapSkipClient(
host: Environment.GetEnvironmentVariable("CAPSKIP_HOST") ?? "127.0.0.1",
port: 8080);
// Read the sitekey off the widget element, in either attribute order.
var html = await http.GetStringAsync(pageUrl);
var widget = Regex.Match(html,
"<[^>]*class=\"(?:[^\"]*\\s)?frc-captcha(?:\\s[^\"]*)?\"[^>]*>").Value;
var sitekey = Regex.Match(widget, "data-sitekey=\"([^\"]+)\"").Groups[1].Value;
// The package name in the script URL decides the version.
var v2 = html.Contains("@friendlycaptcha/sdk");
var v1 = html.Contains("friendly-challenge");
if (v1 == v2)
throw new InvalidOperationException("Read the script tag and set the version by hand.");
var version = v2 ? "v2" : "v1";
try
{
var result = await solver.FriendlyCaptchaAsync(sitekey, pageUrl,
new Dictionary<string, object?> { ["version"] = version });
// A site can rename the field on the widget element.
var renamed = Regex.Match(widget,
"data-(?:form|solution)-field-name=\"([^\"]+)\"").Groups[1].Value;
var field = renamed.Length > 0 ? renamed
: v2 ? "frc-captcha-response" : "frc-captcha-solution";
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
[field] = result.Token!,
});
// Post wherever the form's action attribute points.
var response = await http.PostAsync(pageUrl, body);
Console.WriteLine($"{(int)response.StatusCode} with a {version} token");
}
catch (CapSkip.ValidationException ex)
{
// An empty sitekey or an unknown version, refused before any request.
Console.WriteLine($"not sent: {ex.Message}");
}
catch (CapSkip.TimeoutException)
{
Console.WriteLine("gave up waiting; recaptchaTimeout is 300 seconds");
}Die Version wird einmal bestimmt und zweimal verwendet, für das Lösen und für den Feldnamen, sodass beides nie auseinanderlaufen kann. Findet die Regex nichts, wurde das Widget meist per JavaScript erzeugt und nicht direkt ins HTML geschrieben, und Sie müssen den sitekey aus der gerenderten Seite oder aus dem Skript lesen, das es erzeugt. Den rohen Endpunkt hinter dieser Methode, mit allen Parametern, die er annimmt, finden Sie in der API-Referenz, und alle anderen Methoden, die das Paket bereitstellt, stehen auf der C#-Captcha-Solver-Seite.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| Die Website lehnt einen Token ab, den CapSkip als gelöst zurückgegeben hat | Die falsche Version wurde gelöst, oft der v1-Standard, angewendet auf eine v2-Website | Lesen Sie das Script-Tag und übergeben Sie die Version, oder übergeben Sie die Skriptadresse |
| Die Website lehnt den Token ab, obwohl die Version stimmt | Er ist im Feld der anderen Version gelandet, oder die Website hat das Feld umbenannt | Verwenden Sie das Feld dieser Version oder den Namen aus data-form-field-name bei v2 bzw. data-solution-field-name bei v1 |
| Die Website lehnt den Token ab, und am Widget ist data-api-endpoint gesetzt | Die Website läuft auf einem regionalen Endpunkt, und das Lösen hat den Standard-Endpunkt verwendet | Übergeben Sie den Wert des Attributs als Option api_server |
| Eine ValidationException, bevor überhaupt etwas gesendet wird | Der sitekey oder die Seiten-URL war leer, die Version war nicht v1, v2, 1 oder 2, oder die Optionen enthielten einen Schlüssel, den die Methode nicht annimmt | Prüfen Sie, ob die Regex das Element gefunden hat, korrigieren Sie den Wert für die Version und entfernen Sie die unbekannte Option |
| Kein Element frc-captcha im HTML, das Sie heruntergeladen haben | Die Seite erzeugt das Widget per JavaScript | Lesen Sie den sitekey aus der gerenderten Seite oder aus dem Skript, das es aufbaut |
| Ein Token, der einmal funktioniert hat, scheitert beim zweiten Absenden | Friendly Captcha akzeptiert jede Antwort nur einmal, und Antworten laufen ab | Lösen Sie für jedes Absenden neu und senden Sie sofort ab |
| Lösungen, die im Verlauf eines langen Durchlaufs immer langsamer werden | Der Dienst erhöht die Arbeit für eine Adresse, die er schon oft gesehen hat | Verteilen Sie die Lösungen auf einen Proxy-Pool |
| Eine NetworkException beim ersten Lösen | CapSkip läuft nicht, oder Host und Port sind falsch | Starten Sie CapSkip und prüfen Sie dann, ob es im Local-Modus oder im Server-Modus laufen soll |
| Der Build scheitert an einer mehrdeutigen TimeoutException | CapSkip und System definieren beide diesen Kurznamen | Schreiben Sie CapSkip.TimeoutException vollständig aus oder fangen Sie CapSkipError |
FAQ
Brauche ich auf meiner Seite einen Browser, um Friendly Captcha in C# zu lösen?
Nein. Ihr Code braucht einen HttpClient und sonst nichts. CapSkip erledigt die Arbeit auf dem Rechner, auf dem es läuft, und bei v2 heißt das, dass das Widget dort in einem echten Browser ausgeführt wird. Das ist auch ein Grund dafür, dass die Methode das längere Timeout verwendet. Ihre Seite sendet einen sitekey, eine URL und eine Version und bekommt eine Zeichenfolge zum Absenden zurück, es läuft also problemlos in einem Worker-Service oder einem geplanten Job.
Worin unterscheidet sich das vom Lösen von ALTCHA?
Beides ist Proof of Work, und damit endet die Ähnlichkeit auch schon. Bei ALTCHA übergeben Sie die Challenge oder den Endpunkt, von dem sie kommt, das Hashing dauert Millisekunden, und die Methode läuft auf dem Standard-Timeout von 120 Sekunden. Bei Friendly Captcha legt der Dienst die Schwierigkeit pro Anfrage fest und erhöht sie für viel genutzte Adressen, v2 wird in einem Browser gelöst, und die Methode läuft auf dem Timeout von 300 Sekunden. ALTCHA hat außerdem einen Feldnamen, wo Friendly Captcha zwei hat. Wie das bei ALTCHA aussieht, zeigt der ALTCHA-Leitfaden für C#.
Kann eine .NET-Anwendung auf einer gehosteten Plattform den Solver erreichen?
Ja. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, damit er auf einer Netzwerkadresse statt auf Loopback lauscht, lesen Sie diese Adresse in Ihrem Code aus CAPSKIP_HOST und übergeben Sie sie an den Client. Ein Container-Host, ein VPS, ein CI-Agent und ein Managed-App-Service verbinden sich alle auf dieselbe Weise, über dieselbe HTTP-API. Verwenden Sie eine statische öffentliche IP mit einer Firewall-Regel, wenn der Weg über das Internet führt. Der Solver bleibt auf Hardware, die Ihnen gehört, an der Lizenz und der Zahl der Lösungen ändert sich also nichts.
Kann ich Tokens im Voraus auf Vorrat lösen?
Nicht sinnvoll. Jede Antwort wird nur einmal akzeptiert und läuft ab, und Friendly Captcha meldet beide Fälle der Website als Fehlschlag. Ein Vorrat wird so zu einem Stapel von Ablehnungen. Lösen Sie erst, wenn Sie kurz vor dem Absenden stehen, und lassen Sie stattdessen die Nebenläufigkeit die Arbeit machen: Der Client ist asynchron, ein Task.WhenAll über mehrere Lösungen führt sie also parallel aus.
Die Kurzfassung
Um Friendly Captcha in C# zu lösen, suchen Sie das Widget-Skript, bestimmen daran v1 oder v2 und übergeben diese Version zusammen mit sitekey und Seiten-URL an FriendlyCaptchaAsync. Setzen Sie den Token bei v1 in frc-captcha-solution oder bei v2 in frc-captcha-response, sofern die Website das Feld nicht umbenannt hat, und senden Sie ihn einmal und sofort ab. Nutzen Sie auch den EU-Endpunkt, wenn das Widget ihn verwendet, rechnen Sie mit Lösezeiten von Sekunden statt Millisekunden, und wechseln Sie in den Server-Modus, sobald der aufrufende Code den Rechner des Solvers verlässt.
- Wie der Typ funktioniert und was der Solver abdeckt: die Solver-Seite für Friendly Captcha.
- Alle anderen Methoden, die das .NET-Paket bereitstellt: die C#- und .NET-Solver-Seite.
Noch eine letzte Sache, die bestimmt, wie Sie Wiederholungen angehen. Wenn ein Token abgelehnt zurückkommt, ist die ehrliche Abhilfe fast immer ein neues Lösen mit der richtigen Version, und da ein lokaler Captcha-Löser auf einem Rechner läuft, der Ihnen bereits gehört, kostet dieser erneute Versuch ein paar Sekunden und keine weitere Zeile auf irgendjemandes Rechnung.
