استكشاف الأخطاء

التعامل مع reCAPTCHA v2 وv3 على نفس الصفحة

عندما تظهر نسختا reCAPTCHA — v2 وv3 — على الصفحة نفسها، فالقاعدة العملية واضحة: ابدأ بحل v3 لأنه يعمل تلقائياً في الخلفية، ولا تنتقل إلى v2 إلا إذا انخفضت درجة v3 وظهر التحدي المرئي. ما يربك سكربتات الأتمتة أن كل إصدار نوع مستقل بمفتاح موقع وطريقة إرسال مختلفين، فلا يكفي استدعاء واحد لتغطية الحالتين. نغطي هنا كشف النمط المزدوج على أي صفحة، واستراتيجيات حل الإصدارين معاً عبر API، والحالات الخاصة التي تستحق الانتباه.


متى يجمع الموقع بين v2 وv3 ولماذا

User visits page
    ↓
reCAPTCHA v3 runs invisibly in background
    ↓
Score returned to server (e.g., 0.4)
    ↓
Score below threshold (e.g., < 0.7)?
    ├─ YES → Show reCAPTCHA v2 checkbox/image challenge
    └─ NO  → Allow action without visible CAPTCHA

يمرّ القرار داخل الموقع بأربع خطوات متتابعة:

  1. يعمل v3 غير مرئي ويحسب درجة ثقة للزيارة.
  2. يرسل الموقع الدرجة إلى خادمه ويقارنها بعتبته.
  3. إن تجاوزت الدرجة العتبة، يمرّ الإجراء دون اختبار مرئي.
  4. إن قلّت عنها، يظهر تحدي v2 ويصبح حله شرطاً لإتمام الإجراء.

وبهذا يوازن الموقع بين مصلحتين متقابلتين:

  • غالبية الزوّار ذوو درجة v3 المرتفعة يمرّون دون أي اختبار، فيبقى الاحتكاك شبه معدوم.
  • الزيارات المشبوهة ذات الدرجة المنخفضة تُواجَه بتحدي v2 كطبقة تحقق إضافية.

ثلاثة أنماط شائعة للتنفيذ المزدوج

النمط متى يظهر v2 مفاتيح الموقع
تقييم مسبق ثم احتياطي عند انخفاض درجة v3 مفتاحان مختلفان
مفاتيح حسب الإجراء في صفحات محددة مفتاح لكل إجراء
سكربت واحد بوضعين حسب منطق الصفحة مفتاحان في سكربت واحد

النمط الأول: تقييم v3 مسبقاً ثم v2 كإجراء احتياطي

  • النمط الأكثر انتشاراً، خصوصاً على صفحات تسجيل الدخول.
  • يعمل v3 أولاً ولا يظهر v2 إلا إذا انخفضت الدرجة عن عتبة الموقع.
<!-- Both scripts loaded -->
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script src="https://www.google.com/recaptcha/api.js?render=explicit" async defer></script>

<form id="loginForm">
    <!-- v2 widget (hidden initially) -->
    <div id="recaptcha-v2-container" style="display:none;">
        <div class="g-recaptcha" data-sitekey="V2_SITE_KEY"></div>
    </div>
    <button type="submit">Login</button>
</form>

<script>
// First attempt: v3 invisible
grecaptcha.ready(function() {
    grecaptcha.execute('V3_SITE_KEY', {action: 'login'}).then(function(v3Token) {
        fetch('/api/verify-v3', {
            method: 'POST',
            body: JSON.stringify({token: v3Token})
        })
        .then(r => r.json())
        .then(data => {
            if (data.score < 0.7) {
                // Score too low → show v2 fallback
                document.getElementById('recaptcha-v2-container').style.display = 'block';
                grecaptcha.render('recaptcha-v2-container', {sitekey: 'V2_SITE_KEY'});
            } else {
                // Score OK → submit form directly
                document.getElementById('loginForm').submit();
            }
        });
    });
});
</script>

النمط الثاني: مفاتيح مواقع مختلفة حسب الإجراء

  • يراقب v3 كل الصفحات بدرجة سلبية دون احتكاك.
  • يُفرض v2 على إجراءات محددة عالية الحساسية مثل الدفع.
Homepage → v3 only (passive score)
Login page → v3 assessment, v2 fallback
Checkout → v2 always (high security)
Contact form → v3 only

النمط الثالث: سكربت واحد بوضعين

  • يُحمّل سكربت reCAPTCHA واحد فقط على الصفحة.
  • يتولّى الإصدارين v2 وv3 معاً باستخدام مفتاحين مختلفين.
<script src="https://www.google.com/recaptcha/api.js?render=V3_SITE_KEY"></script>
<script>
    // v3 execute
    grecaptcha.execute('V3_SITE_KEY', {action: 'login'});

    // v2 render (uses a different site key)
    grecaptcha.render('v2-container', {sitekey: 'V2_SITE_KEY'});
</script>

كيف تكتشف النمط المزدوج برمجياً

الكشف باستخدام Python

import requests
import re

def detect_dual_recaptcha(url):
    """Detect if a page uses both reCAPTCHA v2 and v3."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/120.0.0.0 Safari/537.36",
    }
    html = requests.get(url, headers=headers, timeout=15).text

    result = {
        "has_v3": False,
        "has_v2": False,
        "v3_site_key": None,
        "v2_site_key": None,
        "dual": False,
        "pattern": None,
    }

    # Detect v3 (render parameter or enterprise.execute)
    v3_match = re.search(r"api\.js\?render=([A-Za-z0-9_-]+)", html)
    if v3_match and v3_match.group(1) != "explicit":
        result["has_v3"] = True
        result["v3_site_key"] = v3_match.group(1)

    # Detect v3 in execute calls
    v3_execute = re.search(
        r"grecaptcha\.(?:enterprise\.)?execute\s*\(\s*['\"]([^'\"]+)['\"]",
        html,
    )
    if v3_execute:
        result["has_v3"] = True
        if not result["v3_site_key"]:
            result["v3_site_key"] = v3_execute.group(1)

    # Detect v2 (g-recaptcha class or explicit render)
    v2_match = re.search(r'data-sitekey="([^"]+)"', html)
    if v2_match:
        key = v2_match.group(1)
        if key != result.get("v3_site_key"):
            result["has_v2"] = True
            result["v2_site_key"] = key

    # Check for explicit v2 render
    v2_render = re.search(
        r"grecaptcha\.render\s*\([^,]+,\s*\{[^}]*sitekey:\s*['\"]([^'\"]+)",
        html,
    )
    if v2_render:
        result["has_v2"] = True
        if not result["v2_site_key"]:
            result["v2_site_key"] = v2_render.group(1)

    result["dual"] = result["has_v3"] and result["has_v2"]

    if result["dual"]:
        # Determine pattern
        if "display:none" in html or "display: none" in html:
            result["pattern"] = "v3_pre_assessment_v2_fallback"
        else:
            result["pattern"] = "v2_v3_simultaneous"

    return result

detection = detect_dual_recaptcha("https://example.com/login")
print(detection)

الكشف باستخدام Node.js

const axios = require("axios");

async function detectDualRecaptcha(url) {
    const { data: html } = await axios.get(url, { timeout: 15000 });

    const result = {
        hasV3: false,
        hasV2: false,
        v3SiteKey: null,
        v2SiteKey: null,
        dual: false,
    };

    // v3 detection
    const v3Match = html.match(/api\.js\?render=([A-Za-z0-9_-]+)/);
    if (v3Match && v3Match[1] !== "explicit") {
        result.hasV3 = true;
        result.v3SiteKey = v3Match[1];
    }

    // v2 detection
    const v2Match = html.match(/data-sitekey="([^"]+)"/);
    if (v2Match && v2Match[1] !== result.v3SiteKey) {
        result.hasV2 = true;
        result.v2SiteKey = v2Match[1];
    }

    result.dual = result.hasV3 && result.hasV2;
    return result;
}

detectDualRecaptcha("https://example.com/login").then(console.log);

استراتيجيات حل reCAPTCHA المزدوج

تخيّل متجراً إلكترونياً في السوق الخليجي يفعّل v3 على صفحة إتمام الشراء أيام العروض، فإذا انخفضت درجة الزيارة الآلية ظهر تحدي v2. تحتاج حينها إلى مسارات حل موازية للطلبات المتزامنة؛ ولأن CaptchaAI يعمل بنموذج تسعير قائم على الـ Threads — تبدأ خطة BASIC من 15 دولاراً شهرياً بخمسة Threads وحلول غير محدودة لكل Thread — ترفع عدد الطلبات المتزامنة دون رسوم لكل عملية حل.

اختر استراتيجيتك حسب سلوك الموقع:

  1. ابدأ بـ v3 ثم انتقل إلى v2 عند الحاجة إن كان الاحتياطي نادر الظهور.
  2. انتقل مباشرة إلى v2 إن كان يظهر دائماً لحركة مرورك الآلية.
  3. اعتمد متصفحاً حقيقياً في التدفقات المعقّدة التي يصعب تتبّعها بالطلبات وحدها.

الاستراتيجية الأولى: حل v3 أولاً ثم v2 عند الحاجة

  • يحاكي هذا الأسلوب تدفّق الموقع نفسه خطوة بخطوة.
  • يحل v3 أولاً وينتقل إلى v2 فقط إذا تفعّل الاحتياطي.
import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_v3(site_key, page_url, action="login"):
    """Solve reCAPTCHA v3 and return token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "version": "v3",
        "action": action,
        "json": 1,
    }).json()

    task_id = submit["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1,
        }).json()
        if result.get("status") == 1:
            return result["request"]

    raise TimeoutError("v3 solve timeout")

def solve_v2(site_key, page_url):
    """Solve reCAPTCHA v2 and return token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1,
    }).json()

    task_id = submit["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1,
        }).json()
        if result.get("status") == 1:
            return result["request"]

    raise TimeoutError("v2 solve timeout")

def solve_dual_recaptcha(v3_key, v2_key, page_url, action="login"):
    """Handle dual reCAPTCHA: try v3, fall back to v2."""
    # Step 1: Try v3
    v3_token = solve_v3(v3_key, page_url, action)

    # Step 2: Submit v3 token to target
    response = requests.post(f"{page_url}/verify", data={
        "g-recaptcha-response": v3_token,
    })

    # Step 3: Check if v2 fallback is needed
    if "recaptcha" in response.text.lower() and v2_key:
        print("v3 score too low — v2 fallback triggered")
        v2_token = solve_v2(v2_key, page_url)
        return {"version": "v2", "token": v2_token}

    return {"version": "v3", "token": v3_token}

result = solve_dual_recaptcha(
    v3_key="6LcExample_v3_key",
    v2_key="6LcExample_v2_key",
    page_url="https://example.com/login",
)
print(f"Solved with {result['version']}")

الاستراتيجية الثانية: الانتقال مباشرة إلى v2

  • تناسب المواقع التي تُظهر v2 دائماً لحركة المرور الآلية.
  • تبدأ من v2 مباشرة دون إنفاق وقت أو رصيد على v3.
# If you consistently fail v3 assessment, just solve v2 directly
token = solve_v2(v2_site_key, page_url)
submit_form(token)

الاستراتيجية الثالثة: الحل داخل متصفح حقيقي

  • يتولّى المتصفح إظهار v2 وحقن رمزه كما يراه المستخدم فعلاً.
  • مناسب للتطبيقات المعقّدة التي يصعب تتبّعها بالطلبات وحدها.
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

driver = webdriver.Chrome()
driver.get("https://example.com/login")
time.sleep(3)

# Check if v2 widget is visible
v2_visible = driver.execute_script("""
    const container = document.querySelector('.g-recaptcha');
    if (!container) return false;
    const style = window.getComputedStyle(container.parentElement);
    return style.display !== 'none' && style.visibility !== 'hidden';
""")

if v2_visible:
    # v2 is showing — solve v2
    sitekey = driver.find_element(
        By.CSS_SELECTOR, "[data-sitekey]"
    ).get_attribute("data-sitekey")
    token = solve_v2(sitekey, driver.current_url)
    driver.execute_script(
        f'document.getElementById("g-recaptcha-response").value = "{token}";'
    )
else:
    # v3 only — solve v3
    # Extract v3 key from page source
    v3_key = driver.execute_script(
        "return document.querySelector('script[src*=\"render=\"]')"
        ".src.match(/render=([^&]+)/)[1];"
    )
    token = solve_v3(v3_key, driver.current_url)
    # Inject v3 token into the form
    driver.execute_script(f"""
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = 'g-recaptcha-response';
        input.value = '{token}';
        document.querySelector('form').appendChild(input);
    """)

driver.find_element(By.CSS_SELECTOR, "form").submit()

حالات خاصة تستحق الانتباه

مفتاحا موقع مختلفان في الصفحة نفسها

  • مفتاح v3 يظهر في رابط السكربت ?render=KEY وفي grecaptcha.execute('KEY', ...).
  • مفتاح v2 يظهر في data-sitekey="KEY" على عنصر واجهة المستخدم.
  • استخدام المفتاح الخطأ مع الإصدار الخطأ يُنتج رموزاً غير صالحة وطلبات مرفوضة.

reCAPTCHA Enterprise مع احتياطي v2

  • تعتمد بعض تطبيقات Enterprise على v3 Enterprise في التقييم.
  • تبقى v2 للتحديات المرئية، فمرّر معلمة enterprise كما يلي.
# Detect and handle Enterprise + v2 combo
if "recaptcha/enterprise.js" in html:
    # Use enterprise parameter for v3
    v3_params = {"enterprise": 1, "version": "v3"}
else:
    v3_params = {"version": "v3"}

أكثر من نموذج في صفحة واحدة

  • كل نموذج (تسجيل دخول، إنشاء حساب) قد يملك نسخته الخاصة من reCAPTCHA بمفتاح مختلف.
  • استخرج مفتاح الموقع من النموذج المستهدف تحديداً كما في المثال التالي.
# Target the login form specifically
login_form = soup.find("form", id="login-form")
widget = login_form.find(attrs={"data-sitekey": True})
sitekey = widget["data-sitekey"]

أسئلة شائعة

كم استدعاء API أحتاج لصفحة تجمع v2 وv3؟

  • استدعاء واحد يكفي إذا مرّ v3 دون تشغيل الاحتياطي.
  • إذا ظهر v2 فستحتاج استدعاءً ثانياً مستقلاً؛ فلكل إصدار مفتاح وطريقة إرسال مختلفان لا يُدمجان في طلب واحد.

هل ظهور v2 بعد إرسال رمز v3 يعني خطأً في الكود؟

  • لا، إنه سلوك متوقّع: رأى الموقع درجة v3 أقل من عتبته فطلب v2 كتحقق إضافي.
  • راقب استجابة الخادم بعد إرسال رمز v3؛ فظهور HTML لعنصر v2 أو إعادة توجيه إلى تحدٍّ مرئي يعني تفعّل الاحتياطي.

هل يمكنني رفع درجة v3 لتجنّب ظهور v2 من الأساس؟

  • تعتمد الدرجة على إشارات كثيرة منها سمعة عنوان IP وسلوك المتصفح.
  • وكيل سكني موثوق وبيئة متصفح واقعية يرفعان الاحتمال، لكن لا سبيل لضمان درجة بعينها، فابقِ مسار v2 الاحتياطي جاهزاً.

ما التكلفة الإضافية للتعامل مع النمط المزدوج على نطاق واسع؟

  • لا تُحتسب رسوم لكل عملية حل مع CaptchaAI، بل تدفع مقابل عدد الـ Threads المتزامنة.
  • قد يضاعف النمط المزدوج الطلبات وقت الاحتياطي، فاختر خطة بعدد Threads يكفي ذروة حركتك.

الخلاصة

النمط المزدوج من reCAPTCHA يعتمد على v3 لتقييم غير مرئي مسبق، وv2 كتحدٍّ مرئي احتياطي عند انخفاض الدرجة. اكتشف وجود الإصدارين عبر فحص معلمة render للإصدار v3 وسمة data-sitekey للإصدار v2. المسار الأمثل للأتمتة أن تحل v3 أولاً مع CaptchaAI وترسل الرمز، ولا تنتقل إلى v2 إلا إذا تفعّل الاحتياطي — ويتطلب كل إصدار استدعاء API مستقلاً بمفتاح موقعه.

مقالات ذات صلة

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.