Cara Memecahkan Halaman Challenge Cloudflare Turnstile di C#

turnstile challenge page - How to Solve Cloudflare Turnstile Challenge Pages in C#

Cloudflare Turnstile hadir dalam dua bentuk, dan kode C# untuk masing-masing berbeda. Widget yang tertanam dalam formulir tidak memerlukan apa pun selain sitekey dan URL. Tantangan interstisial satu halaman penuh memerlukan dua nilai tambahan yang diambil dari halaman, dan ia hanya akan diterima jika Anda mengirimkan kembali user agent yang digunakan pemecah. Lewatkan bagian terakhir itu dan Anda mendapat token yang terlihat sempurna tetapi selalu gagal validasi.

Panduan ini membahas keduanya, dengan kode .NET yang berfungsi.

Widget atau halaman challenge?

Pastikan dulu yang mana yang Anda hadapi sebelum menulis kode apa pun.

Mode widgetHalaman challenge
Apa yang Anda lihatKotak centang di dalam formulir yang masih dapat Anda gunakanHalaman interstisial penuh, biasanya “Checking your browser”
Sisa halamanDimuat secara normalTerblokir sampai challenge terselesaikan
Membutuhkan data / pagedataTidakYa
Membutuhkan user agent yang dikembalikanTidakYa

Jika Anda ragu, halaman demo Turnstile langsung menjalankan varian widget, sehingga Anda dapat membandingkannya dengan apa pun yang sebenarnya Anda hadapi.

Penyiapan

CapSkip berjalan di komputer Anda sendiri, jadi ada layanan lokal yang harus dijalankan sebelum semua ini bekerja. Instal SDK dari NuGet:

# .NET Standard 2.0, so this works on .NET Framework 4.6.1+,
# .NET Core 2.0+, and .NET 6 through 9.
dotnet add package CapSkip

Lalu arahkan klien ke port yang ditampilkan di aplikasi CapSkip:

using CapSkip;

var solver = new CapSkipClient(
    apiKey: "capskip",        // any string when key validation is off
    host: "127.0.0.1",
    port: 8080,
    recaptchaTimeout: 300);   // seconds, also covers Turnstile and GeeTest

Mode widget, kasus yang mudah

Dua argumen dan Anda selesai:

var result = await solver.TurnstileAsync(
    "0x4AAAAAAA...",                  // sitekey from the data-sitekey attribute
    "https://example.com/login");

Console.WriteLine(result.Code);       // cf-turnstile-response token

Lepaskan result.Code ke dalam cf-turnstile-response kolom tersebut lalu kirim formulirnya. Tidak ada lagi yang perlu dilakukan.

Halaman challenge membutuhkan dua nilai tambahan

Tantangan interstitial membawa state per permintaan yang mengikat token. Dua bagian darinya harus ikut dikirim dalam permintaan pemecahan Anda:

  • cData, diteruskan sebagai data
  • chlPageData, diteruskan sebagai pagedata

Keduanya tertanam di halaman tantangan itu sendiri, bukan di atribut formulir, jadi Anda harus membaca halamannya sebelum dapat memecahkannya. Pada interstisial Cloudflare standar, keduanya terekspos pada objek opsi tantangan milik halaman itu sendiri, bersama sitekey.

Nilai-nilai itu juga sekali pakai dan terikat pada pemuatan halaman tertentu. Ambil, pecahkan segera, dan jangan simpan di cache antar percobaan.

using System.Collections.Generic;
using CapSkip;

var result = await solver.TurnstileAsync(
    sitekey,
    pageUrl,
    new Dictionary<string, object?>
    {
        ["data"]     = cData,          // the cData value from the page
        ["pagedata"] = chlPageData,    // the chlPageData value
        ["action"]   = "managed",       // optional, when the page declares one
    });

Console.WriteLine(result.Code);
Console.WriteLine(result.UserAgent);   // you are going to need this

Bagian yang semua orang lewatkan: user agent

Turnstile mengikat token ke fingerprint browser yang menghasilkannya, dan user agent adalah bagian dari itu. CapSkip mengembalikan nilai yang dipakainya di result.UserAgent. Jika Anda kemudian mengirim token itu dari HttpClient mengirim user agent bawaannya sendiri, nilainya tidak cocok dan Cloudflare menolak token yang sebenarnya sepenuhnya valid.

UserAgent hanya terisi untuk Turnstile. Nilainya null untuk setiap tipe CAPTCHA lain, itulah sebabnya hal ini menjebak orang saat mereka memakai ulang helper reCAPTCHA yang sudah bekerja.

using System.Net.Http;
using CapSkip;

var result = await solver.TurnstileAsync(sitekey, pageUrl, options);

using var http = new HttpClient();

// Send back the exact user agent the solve was performed with.
http.DefaultRequestHeaders.UserAgent.ParseAdd(result.UserAgent);

var form = new FormUrlEncodedContent(new[]
{
    new KeyValuePair<string, string>("cf-turnstile-response", result.Code),
});

var response = await http.PostAsync(pageUrl, form);

Jika token Anda ditolak dan Anda sudah mengesampingkan kemungkinan kedaluwarsa pada cData, ini hampir selalu menjadi penyebabnya.

Menangani kegagalan dengan benar

Setiap exception SDK diturunkan dari CapSkipError, sehingga Anda bisa menangkap satu tipe itu atau menangani masing-masing secara terpisah. Ada satu jebakan khusus .NET yang perlu diketahui.

using System;
using CapSkip;

try
{
    var result = await solver.TurnstileAsync(sitekey, pageUrl, options);
}
catch (CapSkip.ValidationException)   { /* bad parameters */ }
catch (NetworkException)              { /* CapSkip is not running */ }
catch (ApiException)                  { /* API returned an error code */ }
catch (CapSkip.TimeoutException)      { /* exceeded recaptchaTimeout */ }
catch (CapSkipError)                  { /* anything else from the SDK */ }

TimeoutException dan ValidationException ada di keduanya System dan CapSkip. Dengan kedua namespace diimpor, penggunaan tanpa kualifikasi atas catch (TimeoutException) mengarah ke System yang satu itu dan diam-diam tidak pernah terpicu. Kualifikasikan seperti di atas, atau cukup tangkap CapSkipError dan periksa isinya.

Memecahkan beberapa sekaligus

Setiap metode mengembalikan Task, sehingga konkurensi .NET biasa berlaku:

var results = await Task.WhenAll(
    solver.TurnstileAsync(sitekeyA, "https://a.example.com"),
    solver.TurnstileAsync(sitekeyB, "https://b.example.com"));

foreach (var r in results)
{
    Console.WriteLine(r.Code);
}

SDK ini juga mengekspor AsyncCapSkip, tetapi di .NET itu hanya alias dari CapSkipClient. Ia ada agar kode yang dipindahkan dari SDK Python tetap dapat dikompilasi. Beralih ke sana tidak memberi keuntungan apa pun, karena I/O .NET sudah asinkron.

Merutekan melalui proxy

Jika challenge-nya sensitif terhadap geolokasi, pecahkan dari jalur jaringan yang sama dengan tempat Anda akan mengirim:

var result = await solver.TurnstileAsync(sitekey, pageUrl,
    new Dictionary<string, object?>
    {
        ["data"]     = cData,
        ["pagedata"] = chlPageData,
        ["proxy"]    = new Proxy("HTTPS", "user:[email protected]:3128"),
    });

Proxy didukung untuk Turnstile, reCAPTCHA dan GeeTest. Proxy tidak didukung untuk CAPTCHA gambar, yang dipecahkan dari byte gambarnya dan tidak pernah menyentuh situs target.

Pertanyaan yang sering diajukan

Apakah saya selalu memerlukan cData dan chlPageData?

Tidak. Hanya untuk tantangan interstisial satu halaman penuh. Widget Turnstile yang berada di dalam formulir tidak memerlukan apa pun selain sitekey dan URL halaman, dan mengirimkan nilai kosong pada sebuah widget justru akan membuat pemecahan gagal, bukan membantu.

Token saya valid tetapi situs tetap menolaknya. Mengapa?

Hampir selalu user agent. Turnstile mengikat token ke fingerprint yang menghasilkannya, jadi Anda harus mengirimnya menggunakan nilai di result.UserAgent. Penyebab paling umum kedua adalah usangnya cData, yang terikat pada satu kali pemuatan halaman.

Apakah ini bekerja di .NET Framework?

Ya. Paket ini menargetkan .NET Standard 2.0, jadi berjalan di .NET Framework 4.6.1 ke atas, .NET Core 2.0+, dan setiap rilis .NET modern. Dari kode sinkron Anda dapat memanggil .GetAwaiter().GetResult(), meskipun menggunakan await dengan benar lebih baik.

Berapa lama seharusnya pemecahan Turnstile berlangsung?

Biasanya beberapa detik. Klien melakukan polling secara internal, mulai dari 250ms lalu melambat hingga pollingInterval, dan menyerah pada recaptchaTimeout yang nilai bawaannya 300 detik. Jika Anda terus-menerus mentok di batas itu, periksa apakah layanan CapSkip benar-benar berjalan alih-alih menaikkan timeout.

Ringkasan

Turnstile widget adalah panggilan dengan dua argumen. Halaman challenge memerlukan data dan pagedata dibaca langsung dari halaman, dan token harus dikirim bersama user agent yang dikembalikan bersamanya. Tangkap CapSkipError alih-alih melawan tabrakan namespace, dan gunakan proxy ketika challenge-nya sensitif terhadap geolokasi.

Cakupan metode lengkap untuk .NET ada di halaman pemecah CAPTCHA C# , referensi parameternya ada di dokumentasi API, dan Dukungan Turnstile membahas bahasa-bahasa lainnya. CapSkip sendiri adalah pemecah captcha tanpa batas yang berjalan secara lokal, sehingga tidak satu pun dari hal di atas dikenai biaya per pemecahan.