Node.js’te ALTCHA Nasıl Çözülür ve Fetch ile Nasıl Gönderilir

solve altcha in node.js - How to Solve ALTCHA in Node.js and Submit It with Fetch

Node.js’te ALTCHA’yı tek bir çağrıyla, yığının hiçbir yerinde tarayıcı olmadan çözebilirsiniz. ALTCHA tanımaya değil iş kanıtına dayanır: site bir challenge yayınlar ve istemcinin, onu karşılayan sayacı bulana kadar hash alması gerekir. Bakılacak hiçbir şey olmadığı için işin içinde WebDriver, headless Chrome ya da user agent yoktur; cevap tahmin edilmez, hesaplanır. CapSkip bu türü 1.2.6 sürümünde ekledi ve Node SDK onu tek bir metot olarak sunuyor. Bu da burayı, tüm akışın sıradan bir HTTP betiği olduğu ender CAPTCHA türü yapıyor: sayfayı çekin, challenge’ı üzerinden okuyun, çözün, token’ı geri gönderin; hepsi global fetch ve tek bir SDK çağrısıyla.

Neye ihtiyacınız var

  • Bir Windows makinesinde çalışan CapSkip 1.2.6 veya üzeri. ALTCHA desteği bu sürümle geldi.
  • Node 18 veya üzeri; paket bunu gerektirir ve aşağıda kullanılan global fetch de buradan gelir. TypeScript tanımları paketin içinde geliyor, dolayısıyla yanında kurulacak bir types paketi yok.
  • Widget’ın bulunduğu sayfanın URL’si ve widget’ın challenge’ını istediği endpoint.
  • Çözücü için bir adres. Local mode yalnızca o cihaza yanıt vermek üzere 127.0.0.1 üzerinden çalışır; Server mode ise başka bir makinenin erişebilmesi için ağ adresinizi veya genel IP’nizi dinler. Hangisinin geçerli olduğunu 4. adım anlatıyor ve ikisi de şurada yer alıyor: bağlantı ayarları.
# npm install capskip
npm install capskip

1. Adım: çözme çağrısı ve challenge nereden geliyor

Tek metot, iki argüman: sayfa URL’si ve ardından challenge’ı taşıyan bir options nesnesi. Endpoint’i verin, CapSkip challenge’ı kendisi çeksin.

// npm install capskip
const { CapSkip } = require('capskip');

const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });

// CapSkip fetches the challenge, then hashes until the counter fits.
const result = await solver.altcha('https://example.com/signup', {
  challengeUrl: 'https://example.com/altcha/challenge',
});

console.log(result.token);   // base64 payload for the form field
console.log(result.number);  // the counter that satisfied it

Sonuçtaki iki alan yalnızca ALTCHA’ya aittir. token, formun istediği base64 yüktür; number ise challenge’ı çözen sayaçtır. code alanı token ile aynı dizeyi taşır, dolayısıyla ikisi de işe yarar; ancak token, gideceği alanın adını taşıdığı için çağrı yerinde daha iyi okunur. GeeTest alanları ve Turnstile user agent’ı burada yer almaz.

Seçenek adının kabul edilen birden fazla yazımı var. Hem challengeUrl hem challenge_url aynı API parametresine ulaşır; aynısı challengeJson ve challenge_json için de geçerlidir. Node dokümanlarının kullandığı ve SDK’nın geri kalanıyla uyuşan yazım camelCase olanıdır, bu yüzden onu tercih edin ve tutarlı kalın; snake_case takma adlar, PHP veya Python kılavuzundan kopyalanan bir örneğin yine de çalışması için var.

Widget’ın istediği endpoint’i bulun

DevTools’u açın, Network sekmesine geçin ve widget’ın bulunduğu sayfayı yeniden yükleyin. Widget, challenge’ı için genellikle içinde altcha geçen bir yola tek bir istek yapar. Aktardığınız şey o istek URL’sidir; döndürdüğü JSON ise challenge belgesidir ve onun yerine bunu da aktarabilirsiniz.

Challenge’ı adlandıran özniteliği tahmin etmeye çalışmayın, çünkü bu öznitelik widget nesilleri arasında değişti. Sayfa kaynağını okuyun.

Widget nesliChallenge’ı adlandıran öznitelik
v1 ve v2Bir endpoint için challengeurl, satır içi challenge için ayrı bir challengejson özniteliği
v3 ve sonrasıchallenge; aynı öznitelik hem bir URL hem de challenge verisini kabul eder
<!-- v1 and v2 name the endpoint on its own attribute -->
<altcha-widget challengeurl="https://example.com/altcha/challenge"></altcha-widget>

<!-- v3 and later put both forms behind one attribute -->
<altcha-widget challenge="https://example.com/altcha/challenge"></altcha-widget>

Üç görünüm stili, yani native, checkbox ve switch, tamamen görseldir. Aynı yükü gönderirler ve fark hiçbir zaman çözücüye ulaşmaz, dolayısıyla hangisine baktığınızı çözmeniz gerekmez. ALTCHA, öznitelikleri şurada belgeliyor: kendi entegrasyon kılavuzu.

Bunun yerine challenge belgesini vermek

Kazıyıcınız challenge’ı sayfadan zaten okuduysa belgeyi aktarın; o zaman hiç ağ isteği yapılmaz.

// No fetch happens: the document is already here.
const result = await solver.altcha('https://example.com/signup', {
  challengeJson: {
    algorithm: 'SHA-256',
    challenge: 'YOUR_CHALLENGE_HASH',
    salt: 'YOUR_SALT',
    signature: 'YOUR_SIGNATURE',
    maxnumber: 1000000,
  },
});

O seçenek, sizin için serileştirilen bir nesne alır; elinizde zaten varsa bir JSON dizesi de alabilir. Hem endpoint’i hem belgeyi göndermek serbesttir ve satır içi belge kazanır, çünkü çekmek yalnızca az önce verdiğinizi yeniden almak olurdu. Yine de iki yol yük altında farklı davranır. Süresi çoktan dolmuş satır içi bir challenge, boşuna hash alınmak yerine anında reddedilir; bir endpoint ise iş kuyrukta beklerken ilki öldüyse çözücünün taze bir challenge çekmesine izin verir.

Çözücünün kapsadığı algoritmalar

Aynı metot her iki kuşağı da karşılar. Eski şema SHA-1, SHA-256, SHA-384 ve SHA-512 ile kapsanır; iş kanıtı v2 ise PBKDF2 ve yinelemeli SHA ile kapsanır. PBKDF2, ALTCHA’nın kendisinin önerdiği varsayılandır; dolayısıyla kapsanan küme, canlı sitelerin büyük çoğunluğudur.

Argon2id ve scrypt istisnadır ve denenmek yerine reddedilir: bunlardan birini kullanan bir görev, saniyenin yaklaşık üçte biri içinde ERROR_CAPTCHA_UNSOLVABLE ile geri döner ve asla yeniden denenmez. Bu bilinçli bir tercih. Bellek yoğun bir fonksiyon, yeniden denemenin düzelteceği bir şey değildir; meşgul görünmektense hemen başarısız olmak daha iyidir. ALTCHA’da bu sonuç, okunamayan bir görseli değil algoritmayı işaret eder.

2. Adım: tüm akışı tarayıcısız, fetch ile yürütün

Render edilecek bir şey olmadığı için challenge’a ihtiyaç duyduğunuz sayfa yalnızca çekebileceğiniz bir belgedir. Bunu açıkça söylemekte fayda var, çünkü her widget CAPTCHA türünde dürüst cevabın içinde bir yerde tarayıcı geçer. Burada geçmiyor. Sayfayı çekin, özniteliği işaretlemeden çıkarın ve doğrudan çözücüye aktarın.

// npm install capskip
const PAGE = 'https://example.com/signup';

// The page is only a document here: no browser, no rendering.
const html = await (await fetch(PAGE)).text();

// v1 and v2 use challengeurl; v3 and later use challenge.
const found = html.match(/(?:challengeurl|challenge)="([^"]+)"/i);
if (!found) throw new Error('no ALTCHA widget on this page');

const result = await solver.altcha(PAGE, { challengeUrl: found[1] });

Bilinen tek bir sayfa için düzenli ifade yeterlidir, bir tarayıcı botu için ise kötü bir fikirdir; yazmadığınız bir işaretlemeyle uğraşır uğraşmaz gerçek bir HTML ayrıştırıcısına geçin. Örneğin amacı ayrıştırma değil, biçimdir: bir istek, bir dize, bir çözüm ve başlatılıp kapatılacak hiçbir süreç yok. Bu tür, Chromium’u başlatmanın maliyetinin çözümü gölgede bırakacağı sunucusuz bir fonksiyonda veya kısa ömürlü bir worker’da da bu yüzden iyi davranır.

v3 özniteliği hakkında tek bir uyarı. Bu öznitelik ya bir URL ya da challenge belgesinin kendisini tutar, dolayısıyla aktarmadan önce hangisini aldığınıza bakın. Değer bir şema yerine süslü parantezle başlıyorsa satır içi bir challenge’dır ve bir önceki bölümdeki belge seçeneğine aittir.

TypeScript kullanıyorsanız sonucu tiplemek

Tanımlar paketin içinde geliyor, dolayısıyla kurulacak bir types paketi yok. Tek bir sonuç tipi, SDK’nın çözdüğü her CAPTCHA türünü kapsar; bu da yalnızca birine ait olan her alanın isteğe bağlı tanımlandığı anlamına gelir. token ve number birer ALTCHA alanı olduğu için derleyici token’ı string veya undefined olarak tipler ve düz bir string bekleyen hiçbir şeye vermenize izin vermez.

// npm install capskip
import { CapSkip, SolveResult, AltchaOptions } from 'capskip';

const options: AltchaOptions = { challengeUrl: found[1] };
const result: SolveResult = await solver.altcha(PAGE, options);

// One check, right after the call, and the type is settled.
if (!result.token) throw new Error('no ALTCHA token on this result');

const token: string = result.token;

Turnstile user agent’ı da size aynı uyarıyı verir; ayrıntısı şurada: Node.js Turnstile kılavuzu, ama sonucu daha serttir: eksik bir user agent size reddedilen bir gönderime mal olur, eksik bir token ise gönderecek hiçbir şeyinizin olmaması demektir. Non-null assertion’a yalnızca kesinlikle eminseniz başvurun, çünkü o, yanlış metodun çağrıldığını size söyleyen tek denetimi susturur.

Tiplerin yakalamayacağı bir şey var. Options arayüzü bir index signature taşır, dolayısıyla yazdığınız fazladan her anahtar derleyici tarafından kabul edilir. Yanlış yazılmış bir seçenek bu yüzden sorunsuz derlenir ve sonra çalışırken patlar, çünkü SDK, ALTCHA’nın kabul etmediği bir parametreyi reddeder. Options nesnesini yukarıdaki gibi tiplemek, en azından bildiği anahtarları denetler.

3. adım: token’ı süresi geçmeden, hiç değiştirmeden geri gönderin

Widget, payload’ını altcha adlı bir form alanında gönderir; dolayısıyla token’ınız da oraya gider. Sessiz sedasız bozulan adım budur.

// Send it exactly as it came back: no trimming,
// no re-encoding, no reordering.
const response = await fetch('https://example.com/signup', {
  method: 'POST',
  body: new URLSearchParams({
    email: '[email protected]',
    altcha: token,
  }),
});

Token, alanları sunucunun kendi HMAC imzasıyla korunan bir JSON belgesinin base64 hâlidir. Her değişiklik onu geçersiz kılar; bu yüzden düzeltme ya da toparlama gibi görünen her şey gönderimi bozar: boşlukları kırpmak, çözüp yeniden kodlamak veya JSON’ı anahtarları farklı sırada olacak şekilde yeniden oluşturmak. Bazı entegrasyonlar payload’ı form alanı yerine bir JSON gövde alanından okur, bu yüzden sayfanın kendi gönderiminin ne yolladığını kontrol edip aynısını yapın.

Bu adımın başarısız olmasının diğer yolu zamanlamadır. Challenge pencereleri kısadır ve bazı siteler onları iki dakika içinde kapatır. Süresi dolduğunda site, cevabı tam olarak yanlış bir cevaba benzeyen çıplak bir doğrulama hatasıyla reddeder ve yanıtta ikisinden hangisinin yaşandığını söyleyen hiçbir şey bulunmaz. Bundan üç alışkanlık korur: challenge’ı uzun bir akışın başında değil, çözmeden hemen önce çekin; token’ı onu çözen aynı iş biriminde gönderin; ve bir kişi formu doldururken asla elinizde token tutmayın.

Sizi sınırlayan şey istemcinin kendi yoklama zaman aşımları değildir, çünkü challenge penceresi ikisinden biri devreye girmeden çok önce kapanır. ALTCHA bir tarayıcı oturumu değil CPU işi olduğu için, daha uzun olan reCAPTCHA’nınki yerine varsayılan yoklama zaman aşımıyla çalışır.

Yapıcı seçeneğiVarsayılanNeyi kapsar
defaultTimeout120 saniyeALTCHA ve görsel CAPTCHA yoklaması
recaptchaTimeout300 saniyereCAPTCHA, Turnstile ve GeeTest yoklaması
pollingIntervalEn fazla 5 saniyeYoklama 0,25 saniyeden başlar ve kademeli olarak bu değere kadar çıkar

4. adım: çözücünün nerede çalıştığı ve bunun hangi bağlantı modunu gerektirdiği

Yukarıdaki örnekler 127.0.0.1 kullanıyor, çünkü Node süreciniz ve çözücü aynı makineyi paylaştığında doğrusu budur. Çağıran kod başka bir yerde çalışır çalışmaz, örneğin bir container’da, bir CI runner’ında, bir VPS’te veya yönetilen bir barındırmada, loopback artık çözücüyü işaret etmez ve ilk çözüm bir NetworkException ile reddedilir.

CapSkip’i Server mode’a alın; o zaman bunun yerine ağ adresinizi veya genel IP’nizi dinler, böylece bunların hepsi aynı HTTP API üzerinden ona erişebilir. Yol internet üzerinden geçiyorsa sabit bir genel IP önerilir; yanında yalnızca beklediğiniz adreslere izin veren bir güvenlik duvarı kuralıyla. Server mode yalnızca çözücünün nerede dinlediğini değiştirir, başka hiçbir şeyi: donanım yine sizindir ve yine sayaçsızdır. Host ve portu ortamdan okuyun ki tek bir derleme her iki yerde de çalışsın. İstemci CAPSKIP_HOST ve CAPSKIP_PORT değerlerini kendi başına okumaz; bu yüzden, aşağıdaki tam örnekte olduğu gibi, bunları yapıcıya geçirin.

Node süreci nerede çalışıyorHangi bağlantı modu
CapSkip makinesinde, bir betik veya yerel bir sunucu olarakLocal modu. 127.0.0.1 gerçekten doğru
Aynı ağdaki başka bir makinedeServer modu, o makinenin özel adresinde
Bir container’da, bir VPS’te veya yönetilen bir platformdaStatik bir genel IP ve bir güvenlik duvarı kuralı ile Server modu

Proxy’lerle ilgili ALTCHA’ya özgü bir not. Burada proxy desteklenir, ancak yalnızca challenge çekme işlemi için kullanılır. Yönlendirilecek bir tarayıcı oturumu olmadığından, iş kanıtının kendisine hiçbir etkisi yoktur.

Tam çalışan örnek

// npm install capskip
import { CapSkip, ApiException, TimeoutException, NetworkException } from 'capskip';

const solver = new CapSkip({
  host: process.env.CAPSKIP_HOST || '127.0.0.1',
  port: Number(process.env.CAPSKIP_PORT || 8080),
});

export async function signUp(email: string) {
  try {
    // Fetch, solve and submit in one unit of work.
    const result = await solver.altcha('https://example.com/signup', {
      challengeUrl: 'https://example.com/altcha/challenge',
    });

    if (!result.token) throw new Error('not an ALTCHA result');

    const response = await fetch('https://example.com/signup', {
      method: 'POST',
      body: new URLSearchParams({ email, altcha: result.token }),
    });

    console.log(response.status, 'after counter', result.number);
  } catch (err) {
    // ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.
    if (err instanceof ApiException) console.log('refused:', err.message);
    else if (err instanceof TimeoutException) console.log('gave up waiting');
    else if (err instanceof NetworkException) console.log('solver unreachable');
    else throw err;
  }
}

Diğer türler aynı biçimde, yalnızca farklı bir metotla çalışır. reCAPTCHA çağrısı bir sitekey ve bir sayfa URL’si alır; Turnstile de aynı şekilde çalışır; GeeTest sayfa URL’sinin yanında bir gt değeri ve bir challenge alır; görsel çözme ise bir dosya yolu, bir URL veya base64 alır. Metotların tam listesi şurada: Node.js CAPTCHA çözücü sayfası, aynı metotlar ise şurada listelenen her resmi pakette de bulunur: SDK sayfası.

Turnstile, tam bir challenge sayfası olarak geldiğinde sitekey’den fazlasına ihtiyaç duyan tek türdür. Ek değerleri şurada ele alınıyor: Node.js Turnstile kılavuzu.

Sık görülen hatalar ve anlamları

GördüğünüzNedenDüzeltme
Derleyici token’ı kabul etmiyor, string veya undefined bir string değildir diyorTek bir sonuç tipi her CAPTCHA türünü kapsar, bu yüzden yalnızca ALTCHA’ya ait alanlar isteğe bağlıdırÇözümden sonra bir kez daraltın, sonra daraltılmış değeri kullanın
Yanlış yazılmış bir seçenek sorunsuz derlenir ve çalışırken patlarOptions arayüzünde bir index signature var, bu yüzden bilinmeyen anahtarlar geçip giderOptions nesnesini ALTCHA options tipiyle tipleyin ve yazımı kontrol edin
token çalışma zamanında undefined okunuyorO alan yalnızca ALTCHA için doldurulurALTCHA metodunu çağırın. Bir ALTCHA sonucunda code alanı aynı dizeyi tutar
Siteden gelen yalın bir doğrulama hatası, token ise sorunsuz görünüyorForm gönderilmeden önce challenge’ın süresi dolduÇekme, çözme ve gönderme işlemlerini tek bir iş biriminde yapın
Yaklaşık saniyenin üçte biri içinde, bir ApiException içinde ERROR_CAPTCHA_UNSOLVABLEChallenge, Argon2id veya scrypt kullanıyorYeniden denenecek bir şey yok. Bu ikisi tasarım gereği reddedilir
Çağrıda bir ValidationExceptionİki challenge seçeneğinden hiçbiri verilmedi ya da ALTCHA’nın kabul etmediği bir seçenek verildiChallenge endpoint’ini ya da challenge belgesini verin ve geri kalan her şeyi çıkarın
İlk çözümde bir NetworkExceptionCapSkip çalışmıyor ya da host ve port yanlışCapSkip’i başlatın, ardından Local modunda mı yoksa Server modunda mı olması gerektiğini kontrol edin
Loglarınızda çözüldüğü görünen bir token’ı form reddediyorBir şey payload’ı yeniden kodladı, kırptı veya sırasını değiştirdiDizeyi hiç dokunmadan, olduğu gibi geçirin

FAQ

Bir ALTCHA sayfası için Puppeteer veya Playwright gerekir mi?

Hayır ve işin işe yarar kısmı da bu. ALTCHA bakılacak bir şey değil, bir hash problemi verir; dolayısıyla iş tamamen CPU işidir ve milisaniyeler içinde biter. İşin içinde tarayıcı, WebDriver veya user agent yoktur. Global fetch kullanan düz bir betik yeterlidir; bu da onun bir worker, bir kuyruk tüketicisi veya Chromium başlatmanın yavaş ve zahmetli olacağı sunucusuz bir fonksiyon içinde rahatça çalışması demektir.

Barındırılan bir platformdaki Node uygulaması çözücüye erişebilir mi?

Evet. Bağlantı ayarlarından CapSkip’i Server mode’a alın ki loopback yerine bir ağ adresini dinlesin, sonra host ortam değişkenini o adrese yöneltin. Bir container, bir CI runner, bir VPS ve yönetilen bir uygulama platformu; hepsi aynı şekilde, aynı HTTP API üzerinden bağlanır. Yol internetten geçiyorsa sabit bir genel IP kullanın ve bir güvenlik duvarı kuralıyla kısıtlayın. Bunların hepsinde çözücü sizin sahip olduğunuz donanımda kalır, dolayısıyla lisans veya çözüm sayısı hakkında hiçbir şey değişmez.

Async istemci birden fazla ALTCHA challenge’ını daha hızlı çözer mi?

Tek başına hayır. Node paketinde async istemci, ikinci bir uygulama değil, sıradan istemcinin bir takma adıdır; dolayısıyla onu içe aktarmak işin yapılış biçiminde hiçbir şeyi değiştirmez. Her metot zaten bir promise döndürür, bu yüzden eşzamanlılık birkaçını birlikte çalıştırıp hepsini beklemekten gelir. Bunu yaparken her fetch’i kendi çözümünün yanında tutun, çünkü challenge’lar birbirinden bağımsız olarak sona erer ve önceden toplu çekilen bir grup, ilk birkaçı hâlâ hash alırken bayatlar.

SDK’yı kullanmak için TypeScript kullanmam şart mı?

Hayır. Tanımlar paketin içinde gelir, dolayısıyla projeniz onları okuyorsa oradadırlar, okumuyorsa görünmezler. Düz CommonJS tam olarak ilk örnekte gösterildiği gibi çalışır ve tek fark, isteğe bağlı token’ın derleyicinin dayattığı bir denetim değil, sizin yazdığınız bir çalışma zamanı denetimine dönüşmesidir. Bu denetimi yazmaya her hâlükârda değer, çünkü undefined bir token, yanlış metodun çağrıldığının en net işaretidir.

Kısa özet

Challenge endpoint’ini widget’tan okuyun, sayfa URL’siyle birlikte tek ALTCHA metoduna aktarın ve token’ı hiç dokunmadan altcha adlı alana geri gönderin. Tipli bir projede token’ı çözümden sonra bir kez daraltın, çünkü tek bir sonuç tipi her CAPTCHA türünü kapsar ve ALTCHA alanları onun üzerinde isteğe bağlıdır. Fetch’i, çözümü ve gönderimi aynı blokta tutun; çünkü challenge penceresi iki dakika içinde kapanabilir ve süresi dolmuş bir challenge tam olarak yanlış bir cevaba benzer. Node süreci çözücüyle aynı makineyi paylaşmayı bıraktığı anda Server mode’a geçin.

Yeniden denemeyi nasıl tasarladığınızı değiştiren son bir nokta. Bir sınırsız captcha çözücü iş kanıtını zaten sahip olduğunuz bir makinede hesapladığı için, süresi geçmiş bir challenge’ı yeniden denemek size kendi CPU’nuzdan yalnızca birkaç milisaniyeye mal olur, başka hiçbir şeye mal olmaz; bu yüzden bayat bir challenge’ı kurtarmaya çalışmak yerine yeni bir challenge çekmeyi rahatça göze alabilirsiniz.