Cara Memecahkan CAPTCHA Widget AWS WAF dengan Node.js

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

Untuk memecahkan CAPTCHA AWS WAF di Node.js pada halaman widget, Anda membaca apiKey widget dan alamat jsapi.js dari halaman, lalu mengirimkannya ke pemecah AWS WAF CapSkip dengan amazonWaf, menukar pasangan voucher yang dikembalikan dengan sebuah token, dan mengirim token itu pada permintaan berikutnya. Widget adalah bentuk kedua yang dipakai AWS WAF. Halaman formulir atau login menggambar puzzle sendiri, sehingga tidak ada halaman HTTP 405 dan tidak ada gokuProps untuk dibaca. Panduan ini menjalankan seluruh loop dengan SDK Node.js CapSkip dan fetch bawaan Node.js. Untuk halaman 405, lihat panduan Python.

Apa yang Anda butuhkan

  • CapSkip 3.0.0 atau lebih baru yang berjalan di PC Windows. Rilis itu menambahkan pemecahan AWS WAF. Lihat panduan penyiapan untuk menjalankannya pertama kali.
  • Node.js 18 atau lebih baru, sesuai yang diminta paketnya, dan paket capskip versi 1.4.0 atau lebih baru. Versi sebelumnya tidak memiliki metode amazonWaf. Kode di bawah dijalankan pada Node.js 24.22.0 dengan capskip 1.4.0. Versi Node.js lain tidak dicoba di sini.
  • Prompt PowerShell. Perintahnya memakai sintaks PowerShell.
  • Halaman milik Anda atau yang boleh Anda otomatisasi, dengan widget CAPTCHA AWS WAF di dalamnya, dan browser dengan developer tools untuk melihatnya sekali.
  • Sebuah alamat untuk pemecah CAPTCHA. Dalam mode Local, CapSkip menjawab di 127.0.0.1 hanya untuk perangkat itu. Dalam mode Server, CapSkip mendengarkan di alamat jaringan atau IP publik Anda, sehingga skrip di PC lain, VPS, atau platform hosting dapat menjangkaunya melalui API. Keduanya diatur di pengaturan koneksi.

Cara pengujiannya: kode dijalankan terhadap tiga server mock (server pengganti yang dibuat khusus untuk pengujian) yang saya tulis, bukan terhadap situs AWS WAF sungguhan. Satu meniru pemeriksaan AWS WAF dari CapSkip yang tercantum dalam Dokumentasi API, satu berperan sebagai situs dengan widget di halaman kontaknya, dan satu lagi sebagai host token AWS. SDK-nya adalah capskip 1.4.0 yang asli. Kode ini tidak dijalankan terhadap aplikasi CapSkip yang asli, dan tidak ada situs AWS WAF sungguhan yang terlibat, jadi tidak ada angka di bawah ini yang merupakan waktu pemecahan, dan saya tidak bisa memastikan bahwa situs sungguhan tertentu menerima token yang dihasilkan alur ini.

Langkah 1: siapkan proyek dan temukan nilai widget

Buat sebuah folder, inisialisasi proyek, dan instal capskip:

npm init -y
npm install capskip

Sebuah halaman widget memuat skrip bernama jsapi.js dan memanggil AwsWafCaptcha.renderCaptcha dengan objek opsi. Menurut Dokumentasi AWS WAF, apiKey dalam objek itu adalah kunci terenkripsi yang mendaftar domain klien yang boleh memakai widget, jadi kunci itu milik satu situs. CapSkip memerlukan dua hal dari halaman: apiKey tersebut, yang disebutnya sitekey, dan alamat lengkap jsapi.js. Halaman widget tidak memiliki gokuProps, jadi Anda tidak mengirim iv dan context. Mengirim alamat jsapi.js tanpa keduanya itulah yang menandai permintaan sebagai pemecahan widget.

Blok pertama membaca keduanya dari HTML halaman. Beberapa situs menyimpan apiKey di bundle JavaScript terpisah. Ketika itu terjadi, fungsi mengembalikan null, dan solusinya adalah mencari apiKey di skrip yang dimuat halaman.

'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 };
}

Alamat skrip pada sebuah halaman bisa relatif atau diawali dua garis miring, jadi fungsi mengubahnya menjadi alamat lengkap dengan URL halaman. TOKEN_BASE di dekat bagian atas blok adalah konstanta yang Anda isi sendiri, dan langkah berikutnya menjelaskan alasannya.

Langkah 2: temukan host token sekali saja

Pasangan voucher ditukar dengan token di host milik AWS sendiri. Pada halaman widget, host itu tidak ada di HTML yang Anda ambil. Buka halaman di browser, buka tab Network, muat ulang, dan cari permintaan ke challenge.js yang dibuat jsapi.js untuk Anda. Folder permintaan itulah yang harus disalin, diakhiri dengan garis miring. Dokumentasi CapSkip menyuruh mengganti challenge.js dengan voucher, dan itulah yang dilakukan kode di langkah berikutnya dengan TOKEN_BASE.

Ini adalah satu kali pencarian per situs, jadi simpan di konfigurasi Anda. Saya tidak bisa mengatakan berapa lama nilainya tetap sama di situs sungguhan. Jika pertukaran di langkah berikutnya mulai gagal, periksa tab Network lagi sebelum Anda mencurigai hal lain.

Langkah 3: pecahkan dan tukar voucher

Metode SDK-nya adalah amazonWaf. Hanya sitekey dan URL halaman yang wajib, dan opsinya membawa alamat jsapi.js. Alamat skrip harus berupa alamat https di host awswaf.com, dan SDK menolak yang lain sebelum permintaan dikirim. Jawabannya berupa objek dengan captcha_voucher dan existing_token, ditambah jawaban mentah di code dan nomor task di captchaId. Metode ini menunggu dengan timeout 300 detik yang lebih panjang, yang juga dipakai reCAPTCHA, karena CapSkip memecahkan puzzle di browser sungguhan. Saya tidak mengukur waktunya.

Pertukaran itu adalah POST ke endpoint voucher dengan pasangan sebagai body JSON. Dokumentasi CapSkip mengirimnya sebagai text/plain dengan header Origin yang cocok dengan situs. Kode milik aplikasi CapSkip sendiri juga mengirim Referer, jadi kode ini mengirim keduanya. fetch milik Node.js memungkinkan Anda menyetel Origin, hal yang tidak diizinkan browser. Dokumentasi SDK menyebutkan bahwa pasangan itu berumur pendek, jadi tukar segera dan jangan pernah menyimpannya.

// 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
}

Untuk mengirim pemecahan melalui proxy, tambahkan opsi proxy pada panggilan: sebuah objek dengan type disetel ke HTTP dan uri disetel ke user:[email protected]:8080. Pada eksekusi mock, nilai itu tiba di in.php sebagai proxy dan proxytype. Satu alamat sudah cukup untuk pengujian, tetapi situs di balik AWS WAF sering memasangkan CAPTCHA dengan pembatasan laju, jadi aliran pemecahan yang stabil dari satu alamat mendapat perlakuan lebih ketat. Dokumentasi API menyarankan daftar proxy di pengaturan CapSkip, atau proxy per panggilan bila token harus berasal dari jaringan tertentu. Pemecah milik CapSkip sendiri mengirim pemuatan halaman, puzzle, dan pertukaran voucher melalui satu proxy, sehingga AWS melihat satu alamat. Jika Anda memberikan proxy pada pemecahan, kirim permintaan Anda sendiri melalui proxy yang sama. Saya tidak bisa menguji bagian itu.

Langkah 4: kirim token seperti yang dilakukan halaman

Pada halaman widget, token adalah token yang sama yang diterima callback onSuccess milik halaman itu. Halaman berbeda-beda dalam cara meneruskannya. Banyak yang mengirimnya sebagai cookie aws-waf-token dan yang lain sebagai header x-aws-waf-token, jadi kirim formulir sekali secara manual dengan tab Network terbuka dan salin apa yang dibawa permintaan halaman. Fungsi di bawah mengirim cookie dan menunjukkan header sebagai alternatif. Field formulir adalah placeholder untuk field Anda.

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' }),
  });
}

Langkah 5: contoh lengkap yang berfungsi

Blok terakhir menyatukan semua potongan menjadi solusi CAPTCHA AWS WAF di Node.js yang lengkap, dengan percobaan ulang dan entry point. Simpan keempat blok kode dalam satu file, satu demi satu, dan jalankan dengan node. Puzzle yang gagal memunculkan ApiException, dan loop mencoba lagi, yang meminta puzzle baru setiap kali. Error lain dilempar, sehingga host yang salah atau pertukaran yang ditolak tidak dicoba ulang secara diam-diam.

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;
});

Pada eksekusi mock, skrip berperilaku seperti ini:

Pengaturan mockYang dilakukan skrip
Setiap puzzle dijawabSatu task, satu pertukaran voucher, lalu status 200 dan teks Message sent
Dua puzzle pertama ditolakDua baris percobaan, lalu status 200 pada task ketiga
Tiga puzzle pertama ditolakTiga baris percobaan, lalu baris error dan exit code 1

Token berfungsi selama waktu imunitas (immunity time) situs. AWS menetapkan 300 detik secara default dan membiarkan pemilik situs memilih dari 60 detik hingga tiga hari. Dokumentasi AWS WAF menjelaskan hal ini. Jika Anda mengirim banyak permintaan, simpan token dan pakai ulang sampai kedaluwarsa, alih-alih memecahkan sekali per permintaan.

Kesalahan umum

KesalahanPenyebabPerbaiki
tidak ada skrip jsapi.js atau apiKey di HTML halamanHalaman membangun widget dari sebuah bundle, sehingga HTML tidak memuatnyaCari apiKey dan jsapi.js di skrip yang dimuat, lalu masukkan nilainya secara manual
ValidationExceptioniv tanpa context, atau alamat skrip yang bukan alamat https lengkap di host awswaf.comJangan kirim iv atau context untuk widget, dan salin alamat jsapi.js persis seperti di halaman
ApiException dengan ERROR_CAPTCHA_UNSOLVABLEPuzzle tidak bisa dijawabKirim lagi. Dokumentasi API menyebutkan bahwa setiap percobaan menghasilkan puzzle baru
ERROR_TASK_NOT_SUPPORTEDDokumentasi API mencantumkan ini untuk tipe task yang dimatikan di CapSkipPeriksa pengaturan AWS WAF di aplikasi CapSkip
ERROR_PROXY_FORMATString proxy tidak bisa diuraiGunakan user:pass@ip:port
ERROR_NO_SLOT_AVAILABLEAPI key memiliki batas laju permintaan dan Anda melampauinyaTunggu beberapa detik lalu kirim lagi
NetworkExceptionCapSkip tidak berjalan, atau host atau port-nya salahJalankan CapSkip dan periksa pengaturan koneksi
voucher exchange failed: HTTP 4xxKemungkinan besar pasangan yang kedaluwarsa atau tidak cocok, atau TOKEN_BASE yang salah. Kode aplikasi CapSkip mengharapkan HTTP 400 dari AWS untuk existing_token yang tidak valid. Saya tidak bisa mereproduksi yang satu ini terhadap AWS sungguhanTukar segera, periksa folder di tab Network, dan kirim task baru
Post formulir masih ditolakToken kedaluwarsa, atau halaman meneruskannya lewat header dan Anda mengirim cookieSalin apa yang dibawa permintaan milik halaman itu

Menjalankan solver di sebuah server

Skrip memerlukan dua hal dari CapSkip: host dan port. Dalam mode Local itu adalah 127.0.0.1 dan PC Anda sendiri. Dalam mode Server, CapSkip mendengarkan di alamat jaringan atau IP publik Anda, sehingga skrip di PC lain, VPS, atau platform hosting dapat memanggilnya melalui API yang sama. Ubah host dan port di konstruktor CapSkip, dan tidak ada hal lain dalam kode yang berubah. IP publik statis disarankan untuk akses jarak jauh yang stabil. Mode Server tetap berjalan di hardware Anda sendiri dan tetap tanpa meteran. Mode ini memang membuka port untuk semua alamat, jadi tambahkan aturan blokir Windows Firewall untuk alamat yang tidak boleh menjangkaunya. Lihat pengaturan koneksi untuk cara beralih mode.

FAQ

Bagaimana saya membedakan halaman widget dari halaman challenge 405?

Halaman challenge menjawab permintaan dengan HTTP 405 dan mendefinisikan gokuProps. Halaman widget adalah halaman biasa yang memuat jsapi.js dan memanggil renderCaptcha, biasanya pada sebuah formulir. Metode amazonWaf yang sama menangani keduanya, dengan nilai yang berbeda.

Bisakah saya memecahkan CAPTCHA AWS WAF di Node.js tanpa browser?

Skrip Anda tidak memerlukan browser. Skrip hanya mengirim permintaan HTTP biasa ke CapSkip dan ke situs, dan CapSkip memecahkan puzzle di browser sungguhan pada mesin tempatnya berjalan, seperti dijelaskan dokumentasi SDK. Anda hanya membuka browser sendiri sekali, untuk membaca nilai dari halaman.

Apakah SDK mengembalikan token secara langsung?

Tidak. SDK mengembalikan captcha_voucher dan existing_token, dan Anda menukarnya di endpoint voucher. API createTask JSON juga menerima AWS WAF, dan beberapa formatnya mengembalikan token secara langsung. Dokumentasi API mencantumkan formatnya.

Bisakah platform hosting menjalankan ini terhadap CapSkip saya?

Bisa, lewat mode Server. Atur CapSkip untuk mendengarkan di jaringan atau IP publik Anda, arahkan konstruktor CapSkip ke host dan port itu, dan loop bekerja sama seperti pada satu PC. Pemecah tetap berjalan di hardware Anda.

Versi singkatnya

Sebuah CAPTCHA AWS WAF di Node.js adalah puzzle yang sama pada widget seperti pada halaman 405, hanya di tempat yang berbeda: baca apiKey dan alamat jsapi.js, panggil amazonWaf, tukar pasangan di endpoint voucher, dan kirim token seperti yang dilakukan halaman. CapSkip adalah bypass captcha yang berjalan di mesin Anda sendiri, sehingga puzzle yang tidak bisa dijawabnya hanya menghabiskan satu percobaan ulang, bukan biaya. Untuk bagian SDK lainnya, lihat halaman pemecah CAPTCHA Node.js.