C# में ALTCHA कैसे हल करें और token को बिना बदले वापस पोस्ट करें

C# में ALTCHA हल करने के लिए पढ़ने जैसा कुछ होता ही नहीं। ALTCHA proof of work है, पहचान नहीं: साइट एक challenge देती है और क्लाइंट को वह संख्या brute-force से खोजनी होती है जो उसे संतुष्ट करे। इसमें कोई इमेज नहीं, कोई ऑडियो नहीं और कोई अनुमान शामिल नहीं होता, जिससे हल निश्चित और तेज़ हो जाता है। या तो उत्तर मिल जाता है, या फिर challenge गलत बना था या पहले ही समाप्त हो चुका था। CapSkip ने ALTCHA को version 1.2.6 में जोड़ा, और .NET SDK इसे एक ऐसे method के रूप में देता है जो पेज URL के साथ challenge लेता है। जो बात असल में लोगों को उलझाती है वह इसके बाद होती है: token को फॉर्म में ठीक उसी रूप में वापस जाना चाहिए जिस रूप में solver ने उसे लौटाया था।
आपको क्या चाहिए
- किसी Windows मशीन पर चल रहा CapSkip 1.2.6 या उसके बाद का संस्करण। ALTCHA सपोर्ट उसी रिलीज़ में आया था।
- CapSkip .NET पैकेज, जो .NET Standard 2.0 को टारगेट करता है, यानी .NET Framework 4.6.1 और उससे ऊपर, .NET Core 2.0 और उससे ऊपर, तथा .NET 6 और उसके बाद के संस्करण।
- उस पेज का URL जिस पर widget लगा है, और वह endpoint जहाँ से वह widget अपना challenge लाता है।
- solver के लिए एक पता। Local मोड केवल उसी डिवाइस के लिए 127.0.0.1 पर जवाब देता है; Server मोड आपके नेटवर्क पते या पब्लिक IP पर सुनता है ताकि कोई दूसरी मशीन उस तक पहुँच सके। स्टेप 4 बताता है कि आपको कौन सा चाहिए, और दोनों यहाँ मिलते हैं: कनेक्शन सेटिंग्स.
# dotnet add package CapSkip dotnet add package CapSkip
स्टेप 1: वह endpoint खोजें जिसे ALTCHA widget कॉल करता है
बाकी सब कुछ इसी एक वैल्यू पर निर्भर करता है, इसलिए इसे सबसे पहले हासिल करें। DevTools खोलें, Network टैब पर जाएँ और उस पेज को रीलोड करें जिस पर widget लगा है। widget अपने challenge के लिए एक रिक्वेस्ट करता है, आमतौर पर ऐसे पाथ पर जिसमें altcha होता है। वही रिक्वेस्ट URL आप solver को देते हैं, और वह endpoint जो JSON लौटाता है वही challenge डॉक्यूमेंट होता है, जिसे आप URL के बजाय पास कर सकते हैं।
जो attribute इसका नाम बताता है उसका अनुमान न लगाएँ, क्योंकि widget की पीढ़ियों के बीच यह बदल चुका है। पेज सोर्स पढ़ें।
| Widget पीढ़ी | वह attribute जो challenge का नाम बताता है |
|---|---|
| v1 और v2 | endpoint के लिए challengeurl, और इनलाइन challenge के लिए अलग challengejson attribute |
| v3 और उसके बाद | challenge, और वही attribute या तो URL लेता है या challenge डेटा |
तीन डिस्प्ले स्टाइल, native, checkbox और switch, पूरी तरह दिखावटी हैं। तीनों वही payload सबमिट करते हैं और यह फ़र्क कभी solver तक नहीं पहुँचता, इसलिए आपको यह पता लगाने की ज़रूरत नहीं कि आप कौन सा देख रहे हैं। ALTCHA widget attributes को यहाँ कवर करता है: इसके अपने इंटीग्रेशन डॉक्स.
स्टेप 2: solve कॉल, और challenge देने के दो तरीके
एक method, दो आर्गुमेंट: पेज URL, फिर एक ऑप्शंस डिक्शनरी जिसमें challenge होता है। इसे endpoint दें और CapSkip आपके लिए challenge ले आता है।
// dotnet add package CapSkip
using CapSkip;
var solver = new CapSkipClient(host: "127.0.0.1", port: 8080);
// CapSkip fetches the challenge, then brute-forces the counter.
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_url"] = "https://example.com/altcha/challenge",
});
Console.WriteLine(result.Token); // base64 payload for the form field
Console.WriteLine(result.Number); // the counter that satisfied itरिज़ल्ट के दो फ़ील्ड केवल ALTCHA के लिए हैं। Token वह base64 payload है जो फॉर्म चाहता है, और Number वह काउंटर है जिसने challenge हल किया। Code प्रॉपर्टी में वही स्ट्रिंग होती है जो Token में है, इसलिए दोनों में से कोई भी काम करता है, लेकिन Token का नाम उसी फ़ील्ड पर है जिसमें वह जाता है और कॉल साइट पर वह बेहतर पढ़ा जाता है। GeeTest के फ़ील्ड और Turnstile user agent यहाँ null रहते हैं।
Number को लॉग करना फ़ायदेमंद है। यह ALTCHA की दोनों पीढ़ियों के लिए रिपोर्ट होता है, भले उनके payload अलग हों: लेगेसी token काउंटर को टॉप लेवल पर रखता है, जबकि proof-of-work v2 token ऐसा नहीं करता और उसे एक solution ऑब्जेक्ट के अंदर रखता है। CapSkip इसे अपने API रिस्पॉन्स के solution ऑब्जेक्ट से पढ़ता है, जो दोनों पीढ़ियों को एक ही तरह रिपोर्ट करता है।
इसके बजाय challenge डॉक्यूमेंट पास करना
अगर आपका कोड पहले ही challenge ले चुका है, तो डॉक्यूमेंट पास करें और कोई नेटवर्क रिक्वेस्ट होगी ही नहीं। जब आप पेज को पहले से स्क्रैप कर रहे हों तो यह तेज़ रास्ता है, और जब challenge किसी endpoint से नहीं बल्कि HTML में एम्बेड होकर आता है तो यही इस्तेमाल करना चाहिए।
// No fetch happens: the document is already here.
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_json"] = new Dictionary<string, object>
{
["algorithm"] = "SHA-256",
["challenge"] = "YOUR_CHALLENGE_HASH",
["salt"] = "YOUR_SALT",
["signature"] = "YOUR_SIGNATURE",
["maxnumber"] = 1000000,
},
});वह ऑप्शन एक डिक्शनरी लेता है, जिसे आपके लिए सीरियलाइज़ कर दिया जाता है, या अगर आपके पास पहले से है तो JSON स्ट्रिंग। endpoint और डॉक्यूमेंट दोनों भेजना मान्य है, और इनलाइन डॉक्यूमेंट जीतता है, क्योंकि फ़ेच करने पर वही चीज़ दोबारा मिलेगी जो आपने अभी दी थी। लोड के तहत दोनों रास्ते अलग तरह से व्यवहार भी करते हैं: जो इनलाइन challenge पहले ही समाप्त हो चुका है उसे बेमतलब हैश करने के बजाय सीधे मना कर दिया जाता है, जबकि endpoint देने पर solver नया challenge ला सकता है, अगर पहला वाला तब मर गया जब जॉब कतार में पड़ा था।
solver किन algorithm को कवर करता है
वही एक method दोनों पीढ़ियों को संभालता है। लेगेसी स्कीम SHA-1, SHA-256, SHA-384 और SHA-512 के साथ कवर होती है, और proof-of-work v2 PBKDF2 तथा iterative SHA के साथ कवर होता है। PBKDF2 वही डिफ़ॉल्ट है जिसकी सिफ़ारिश ALTCHA खुद करता है, इसलिए इससे लाइव साइटों का बहुत बड़ा हिस्सा कवर हो जाता है।
Argon2id और scrypt अपवाद हैं, और उन्हें आज़माने के बजाय मना कर दिया जाता है: इनमें से किसी एक का इस्तेमाल करने वाला task करीब एक तिहाई सेकंड में ERROR_CAPTCHA_UNSOLVABLE के साथ लौट आता है और उसे कभी दोबारा नहीं आज़माया जाता। यह जानबूझकर है। memory-hard फ़ंक्शन ऐसी चीज़ नहीं जिसे दोबारा कोशिश करने से ठीक किया जा सके, इसलिए तुरंत फेल होना व्यस्त दिखने से बेहतर है। ALTCHA के मामले में वह नतीजा किसी न पढ़ी जा सकने वाली इमेज के बजाय algorithm की ओर इशारा करता है, और इस एरर कोड के लिए अलग गाइड मौजूद है: अपनी एक अलग गाइड.
स्टेप 3: token को बिना बदले वापस पोस्ट करें, उसके समाप्त होने से पहले
widget अपना payload altcha नाम के फॉर्म फ़ील्ड में सबमिट करता है, इसलिए आपका token भी वहीं जाता है। यही वह स्टेप है जो चुपचाप टूट जाता है।
// Send it exactly as it came back: no trimming,
// no re-encoding, no reordering.
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
["altcha"] = result.Token!,
});
var response = await http.PostAsync("https://example.com/signup", body);token एक JSON डॉक्यूमेंट का base64 है, जिसके फ़ील्ड सर्वर के अपने HMAC signature के दायरे में आते हैं। कोई भी बदलाव इसे अमान्य कर देता है, इसलिए सफ़ाई जैसी दिखने वाली हर चीज़ सबमिट को तोड़ देगी: व्हाइटस्पेस हटाना, इसे डिकोड करके फिर से एनकोड करना, या keys को अलग क्रम में रखकर JSON दोबारा बनाना। कुछ इंटीग्रेशन payload को फॉर्म फ़ील्ड के बजाय JSON बॉडी फ़ील्ड से पढ़ते हैं, इसलिए देखें कि पेज का अपना सबमिट क्या भेजता है और वैसा ही करें।
यह स्टेप दूसरी तरह से टाइमिंग की वजह से फेल होता है। challenge की विंडो छोटी होती है और कुछ साइटें उसे दो मिनट के अंदर बंद कर देती हैं, और जब कोई विंडो समाप्त हो जाती है तो साइट जवाब को एक सूखी वेरिफ़िकेशन फेल्योर के साथ ठुकरा देती है, जो बिलकुल गलत जवाब जैसी दिखती है। एरर में ऐसा कुछ नहीं होता जो बताए कि दोनों में से हुआ क्या। तीन आदतें इससे बचाती हैं: challenge को लंबे रन की शुरुआत में लाने के बजाय हल करने से ठीक पहले लाएँ, token को उसी काम की इकाई में सबमिट करें जिसने उसे हल किया, और किसी व्यक्ति के फॉर्म भरने के दौरान token को कभी रोककर न रखें।
यहाँ आपको सीमित करने वाली चीज़ क्लाइंट के अपने पोलिंग timeout नहीं हैं, क्योंकि दोनों ही उस दो मिनट की विंडो से कहीं लंबे हैं। ALTCHA ब्राउज़र सेशन नहीं, बल्कि CPU का काम है, इसलिए यह लंबे reCAPTCHA timeout के बजाय डिफ़ॉल्ट पोलिंग timeout पर चलता है।
| कंस्ट्रक्टर ऑप्शन | डिफ़ॉल्ट | यह क्या कवर करता है |
|---|---|---|
| defaultTimeout | 120 सेकंड | ALTCHA और इमेज कैप्चा की पोलिंग |
| recaptchaTimeout | 300 सेकंड | reCAPTCHA, Turnstile और GeeTest की पोलिंग |
| pollingInterval | अधिकतम 5 सेकंड | पोलिंग 0.25 सेकंड से शुरू होती है और बढ़कर इस तक पहुँचती है |
स्टेप 4: solver कहाँ चलता है, और उसके लिए कौन सा कनेक्शन मोड चाहिए
ऊपर दिए सैंपल 127.0.0.1 इस्तेमाल करते हैं, क्योंकि जब आपका कोड और solver एक ही मशीन पर हों तो यही सही है। जैसे ही solver को कॉल करने वाला कोड कहीं और चलता है, जैसे किसी कंटेनर, बिल्ड एजेंट, VPS या मैनेज्ड होस्ट पर, loopback का इशारा solver की तरफ़ नहीं रह जाता, और पहला ही solve एक NetworkException फेंक देता है।
CapSkip को Server मोड पर कर दें और वह इसके बजाय आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि इनमें से कोई भी API के ज़रिए उस तक पहुँच सके। अगर आप इंटरनेट के रास्ते जा रहे हैं तो स्टैटिक पब्लिक IP की सलाह दी जाती है, साथ में ऐसा फ़ायरवॉल नियम जो सिर्फ़ अपेक्षित पतों को ही अनुमति दे। Server मोड सिर्फ़ यह बदलता है कि solver कहाँ सुनता है, और कुछ नहीं: यह अब भी आपका अपना हार्डवेयर है, और अब भी बिना मीटर वाला है। होस्ट को एनवायरनमेंट वेरिएबल से पढ़ें ताकि एक ही बिल्ड दोनों जगह काम करे। क्लाइंट CAPSKIP_HOST को अपने आप नहीं पढ़ता, इसलिए इसे constructor को पास करें, जैसा नीचे दिया गया पूरा उदाहरण करता है।
| C# कहाँ चलता है | कौन-सा कनेक्शन मोड |
|---|---|
| CapSkip मशीन पर, किसी IDE या कंसोल ऐप में | Local mode। 127.0.0.1 सचमुच सही है |
| उसी नेटवर्क पर किसी दूसरी मशीन पर | Server मोड, उस मशीन के प्राइवेट पते पर |
| किसी कंटेनर होस्ट, VPS या मैनेज्ड प्लेटफ़ॉर्म पर | स्टैटिक पब्लिक IP और एक फ़ायरवॉल नियम के साथ Server mode |
प्रॉक्सी को लेकर ALTCHA से जुड़ी एक बात। यहाँ प्रॉक्सी सपोर्टेड है, लेकिन उसका इस्तेमाल सिर्फ़ challenge लाने के लिए होता है। रूट करने के लिए कोई ब्राउज़र सेशन होता ही नहीं, इसलिए खुद proof of work पर इसका कोई असर नहीं पड़ता।
पूरा चलने वाला उदाहरण
// dotnet add package CapSkip
using CapSkip;
var http = new HttpClient();
var solver = new CapSkipClient(
host: Environment.GetEnvironmentVariable("CAPSKIP_HOST") ?? "127.0.0.1",
port: 8080);
try
{
var result = await solver.AltchaAsync(
"https://example.com/signup",
new Dictionary<string, object?>
{
["challenge_url"] = "https://example.com/altcha/challenge",
});
// Submit here, while the challenge is still fresh.
var body = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "[email protected]",
["altcha"] = result.Token!,
});
var response = await http.PostAsync("https://example.com/signup", body);
Console.WriteLine($"{(int)response.StatusCode} after counter {result.Number}");
}
catch (ApiException ex)
{
// ERROR_CAPTCHA_UNSOLVABLE here means Argon2id or scrypt.
Console.WriteLine($"refused: {ex.Message}");
}
catch (CapSkip.TimeoutException)
{
Console.WriteLine("gave up waiting; defaultTimeout is 120 seconds");
}बाकी टाइप भी वैसी ही शक्ल के हैं, बस method अलग है। RecaptchaAsync एक sitekey और पेज URL लेता है, TurnstileAsync तथा GeetestAsync भी उसी तरह काम करते हैं, और इमेज हल करना एक base64 कॉल है। पूरी method सूची यहाँ मौजूद है: C# कैप्चा सॉल्वर पेज.
Challenge-page Turnstile ही एकमात्र टाइप है जिसे sitekey से ज़्यादा की ज़रूरत होती है। उसकी अतिरिक्त वैल्यू यहाँ कवर की गई हैं: C# challenge-page गाइड.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| साइट से एक सूखी वेरिफ़िकेशन फेल्योर, जबकि token ठीक दिखता है | फॉर्म सबमिट होने से पहले ही challenge समाप्त हो गया | लाना, हल करना और सबमिट करना, तीनों एक ही काम की इकाई में करें |
| ApiException के अंदर ERROR_CAPTCHA_UNSOLVABLE, करीब एक तिहाई सेकंड में | challenge Argon2id या scrypt इस्तेमाल करता है | दोबारा कोशिश करने जैसा कुछ नहीं। वे दोनों जानबूझकर मना किए जाते हैं |
| कॉल पर एक ValidationException | कोई भी challenge ऑप्शन नहीं दिया गया, या ऐसा ऑप्शन पास किया गया जो ALTCHA नहीं लेता | challenge endpoint या challenge डॉक्यूमेंट पास करें, और बाकी सब हटा दें |
| पहले solve पर एक NetworkException | CapSkip चल नहीं रहा, या host और port गलत हैं | CapSkip शुरू करें, फिर देखें कि उसे Local मोड में होना चाहिए या Server मोड में |
| रिज़ल्ट पर Token प्रॉपर्टी null दिखाती है | Token सिर्फ़ ALTCHA के लिए भरा जाता है | AltchaAsync कॉल करें। ALTCHA रिज़ल्ट में Code प्रॉपर्टी वही स्ट्रिंग रखती है |
| build किसी अस्पष्ट TimeoutException पर फ़ेल हो जाता है | CapSkip और System, दोनों उस छोटे नाम को परिभाषित करते हैं | पूरा CapSkip.TimeoutException लिखें, या CapSkipError कैच करें |
| फॉर्म ऐसे token को ठुकरा देता है जिसे आपके लॉग हल हुआ दिखाते हैं | किसी चीज़ ने payload को दोबारा एनकोड किया, ट्रिम किया या उसका क्रम बदल दिया | स्ट्रिंग को जैसी है वैसी ही, बिना छेड़े आगे भेजें |
FAQ
क्या C# में ALTCHA हल करने के लिए ब्राउज़र चाहिए?
नहीं, और यही इसकी सबसे काम की बात है। ALTCHA देखने लायक कोई चीज़ नहीं, बल्कि एक हैशिंग समस्या देता है, इसलिए काम सिर्फ़ CPU का है और मिलीसेकंड में पूरा हो जाता है। आपको न WebDriver चाहिए, न हेडलेस Chrome और न कोई user agent। HttpClient वाला एक कंसोल ऐप काफ़ी है, जिसका मतलब यह भी है कि यह किसी वर्कर सर्विस, क्यू कंज़्यूमर या बिल्ड स्टेप के अंदर आराम से चलता है, जहाँ ब्राउज़र चलाना असुविधाजनक होता।
क्या किसी होस्टेड प्लेटफ़ॉर्म पर चल रहा .NET ऐप solver तक पहुँच सकता है?
हाँ। कनेक्शन सेटिंग्स में CapSkip को Server मोड पर कर दें ताकि वह loopback के बजाय किसी नेटवर्क पते पर सुने, फिर CAPSKIP_HOST को उसी पते पर लगा दें। कंटेनर होस्ट, VPS, CI एजेंट या मैनेज्ड ऐप सर्विस, सब उसी तरह जुड़ते हैं, उसी HTTP API के ज़रिए। अगर रास्ता इंटरनेट से होकर जाता है तो स्टैटिक पब्लिक IP इस्तेमाल करें, और उसे फ़ायरवॉल नियम से सीमित करें। इनमें से हर सूरत में solver आपके अपने हार्डवेयर पर ही रहता है, इसलिए लाइसेंस या हल की गिनती में कुछ नहीं बदलता।
मुझे endpoint पास करना चाहिए या challenge डॉक्यूमेंट?
जब तक डॉक्यूमेंट आपके पास पहले से न हो, endpoint ही पास करें। यह ऑप्शन डिक्शनरी की एक ही एंट्री है, एक रिक्वेस्ट बचाता है, और अगर जॉब कतार में रहते हुए challenge बासी हो जाए तो solver खुद नया ले आता है। डॉक्यूमेंट तब पास करें जब आपका स्क्रैपर उसे पेज से पहले ही पढ़ चुका हो, जब challenge किसी endpoint से आने के बजाय HTML में एम्बेड हो, या जब उसे लाने के लिए ऐसी कुकीज़ या हेडर चाहिए जो आपके कोड के पास हैं और solver के पास नहीं। उस आख़िरी सूरत में प्रॉक्सी ऑप्शन के बारे में जानना काम आता है, क्योंकि ALTCHA में वह फ़ेच पर और सिर्फ़ फ़ेच पर लागू होता है।
मेरा काउंटर हर बार अलग संख्या क्यों होता है?
क्योंकि वह उस ख़ास challenge का उत्तर है, साइट की कोई विशेषता नहीं। हर challenge अपना salt लेकर आता है, इसलिए उसे संतुष्ट करने वाली संख्या हर बार जारी होने पर बदल जाती है, और वह उस maxnumber तक कहीं भी हो सकती है जितनी challenge इजाज़त देता है। बड़े काउंटर का मतलब बस इतना है कि ज़्यादा हैशिंग की ज़रूरत पड़ी, जो कुछ अतिरिक्त मिलीसेकंड के रूप में दिखता है और इससे ज़्यादा कुछ नहीं। लॉग में यह इस सबूत के तौर पर काम आता है कि काम सचमुच हुआ, और कैश करने की चीज़ के तौर पर यह बेकार है।
संक्षेप में
widget से challenge endpoint पढ़ें, उसे पेज URL के साथ उस एक ALTCHA method को दें, और token को बिना छेड़े altcha नाम के फ़ील्ड में वापस पोस्ट करें। लाना, हल करना और सबमिट करना, तीनों एक ही ब्लॉक में रखें, क्योंकि challenge की विंडो दो मिनट के अंदर बंद हो सकती है और समाप्त हो चुका challenge बिलकुल गलत जवाब जैसा दिखता है। ERROR_CAPTCHA_UNSOLVABLE की उम्मीद सिर्फ़ Argon2id और scrypt से रखें, जिन्हें आज़माने के बजाय सीधे मना कर दिया जाता है। जिस पल कॉल करने वाला कोड solver के साथ एक ही मशीन पर नहीं रहता, उसी पल Server मोड पर चले जाएँ।
- challenge क्या है और यह टाइप कैसे काम करता है: ALTCHA सॉल्वर पेज.
- .NET पैकेज जो बाकी सभी method देता है: C# और .NET सॉल्वर पेज.
एक आख़िरी बात, जो बदल देती है कि आप retry को कैसे डिज़ाइन करेंगे। चूँकि एक लोकल captcha सॉल्वर proof of work उसी मशीन पर करता है जो पहले से आपकी अपनी है, इसलिए समाप्त हो चुके challenge को दोबारा आज़माने में आपके अपने CPU के कुछ मिलीसेकंड के अलावा कुछ खर्च नहीं होता, इसलिए आप बासी challenge को सँभालने के बजाय नया challenge लाने की छूट रख सकते हैं।
