الشروحات المعمقة

أوضاع أداة Cloudflare Turnstile: المُدار وغير التفاعلي وغير المرئي

إذا كنت تُؤتمت تدفقاً محمياً بـ Cloudflare Turnstile، فإليك الخلاصة أولاً: الأوضاع الثلاثة — المُدار، وغير التفاعلي، وغير المرئي — تُنتج جميعها الرمز نفسه (cf-turnstile-response)، وتُحلّ بالطريقة نفسها في CaptchaAI عبر الطريقة turnstile. أي أن الوضع لا يغيّر شيئاً في مرحلة الحل. فأين يكمن الفرق إذن؟ في الاكتشاف: كيف تعرف أيّ وضع يقف أمامك، ومن أين تلتقط مفتاح الموقع، ومتى يصبح الرمز جاهزاً للإرسال. نشرح هنا الأوضاع الثلاثة من زاوية المطوّر الذي يبني أتمتة موثوقة، مع كود جاهز لاكتشاف كل وضع وحلّه.

لماذا يهمّ الوضع للأتمتة رغم تطابق الرمز؟

من زاوية الحل الأوضاع الثلاثة متكافئة، لكنها تختلف في ثلاث نقاط تهمّ أي سكربت أتمتة:

  • كيف يُركَّب عنصر الأداة داخل الصفحة
  • متى تصبح قيمة الرمز متاحة للإرسال
  • ما البديل الذي يقع عند فشل التحدي

تجاهُل هذه الفروق هو أشيع سبب لسكربت يعمل على موقع ويتعطّل على آخر بالحماية نفسها. خذ مثالاً واقعياً: فريق يبني اختبارات QA آلية لبوابة دفع تخدم السوق الخليجي. صفحة تسجيل الدخول تعتمد الوضع المُدار، فيظهر مربع الاختيار ويختفي بحسب سمعة عنوان IP، بينما يعمل نموذج الاشتراك في النشرة على الموقع نفسه بالوضع غير التفاعلي بلا نقر. سكربت واحد يفترض شكلاً ثابتاً للأداة سيفشل على إحدى الصفحتين حتماً.

القاعدة العملية: أعِد اكتشاف الوضع في كل صفحة بدل افتراض شكل ثابت للأداة.

الوضع المُدار: الإعداد الافتراضي والأكثر شيوعاً

في الوضع المُدار تترك القرار لـ Cloudflare ليحدد مستوى التحدي لكل زائر. معظم الزوار يمرّون بصمت، وحركة المرور المشبوهة ترى مربع اختيار، أما الأعلى خطورة فقد تواجه تحدياً أعقد أو تُحجب. وهو الوضع الافتراضي الذي يُفعَّل ما لم تُحدَّد سمة صريحة، ويبدأ بوسم <div> واحد مع سكربت Turnstile:

<!-- Managed mode (default) -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-theme="light">
</div>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>

ما الذي تراه أدوات الأتمتة

يتكيّف الوضع المُدار وفق إشارات الطالب مثل سمعة عنوان IP وبصمة المتصفح:

السمعة تظهر الأداة على هيئة
ثقة عالية تمريرة غير مرئية (بلا أي واجهة ظاهرة)
ثقة متوسطة أداة مربع اختيار (انقر للتحقق)
ثقة منخفضة تحدٍّ تفاعلي أو حجب

لهذا يُعدّ الوضع المُدار الأكثر شيوعاً والأكثر تقلّباً؛ فقد تظهر الأداة أو لا بحسب إشارات المتصفح. وبما أنه لا يحمل سمة صريحة، تكتشفه بالنفي: وجود cf-turnstile دون أي سمة data-appearance يعني على الأرجح أنك أمامه:

def is_managed_mode(html):
    """Check if Turnstile is using managed mode (default)."""
    # Managed mode is the default — no explicit mode attribute
    has_turnstile = "cf-turnstile" in html
    has_explicit_mode = 'data-appearance="interaction-only"' in html or \
                        'data-appearance="always"' in html or \
                        'appearance: "interaction-only"' in html
    return has_turnstile and not has_explicit_mode

الوضع غير التفاعلي: تحقّق صامت بلا نقر

لا يعرض الوضع غير التفاعلي مربع اختيار ولا أي عنصر قابل للنقر. يُشغّل تحدّي إثبات العمل في الخلفية ولا يُظهر سوى مؤشّر تحميل، وإن تعذّر إكماله دون تفاعل فإنه يفشل بدلاً من التصعيد إلى مربع اختيار. تفعّله بسمة data-appearance="interaction-only":

<!-- Non-interactive mode -->
<div class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-appearance="interaction-only">
</div>

أو عبر واجهة JavaScript البرمجية:

turnstile.render('#turnstile-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    appearance: 'interaction-only',
    callback: function(token) {
        document.getElementById('cf-turnstile-response').value = token;
    },
});

يمرّ الوضع بالتسلسل التالي دون واجهة مرئية:

Page loads → Widget initializes
    ↓
Background proof-of-work runs
    ↓
Success → Token generated (no visible UI)
    OR
Failure → Widget reports error (no fallback to checkbox)

يشيع استخدامه في سياقات منخفضة الاحتكاك مثل:

  • نماذج التعليقات وأدوات ملاحظات المستخدمين
  • الاشتراك في النشرات البريدية
  • الإجراءات منخفضة القيمة حيث يجب أن يبقى الاحتكاك عند حدّه الأدنى
  • نقاط نهاية API المحمية من جانب المتصفح

الوضع غير المرئي: بلا أي أثر بصري

في الوضع غير المرئي لا يظهر أي عنصر حاوية داخل إطار العرض. تعمل الأداة عند تحميل الصفحة (أو عبر مشغّل برمجي) وتُنتج الرمز دون أي إشارة بصرية. تضبطه بالحجم data-size="invisible":

<!-- Invisible mode — container is hidden -->
<div id="turnstile-invisible"
     class="cf-turnstile"
     data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg"
     data-size="invisible">
</div>

أو بالكامل عبر JavaScript:

// Programmatic invisible Turnstile
turnstile.render('#hidden-container', {
    sitekey: '0x4AAAAAAAC3DHQhMMQ_Rxrg',
    size: 'invisible',
    callback: function(token) {
        // Token ready — submit form automatically
        submitForm(token);
    },
    'error-callback': function() {
        // Challenge failed
        console.error('Invisible Turnstile failed');
    },
});

يصعب اكتشاف Turnstile غير المرئي لأن الحاوية بلا أبعاد ظاهرة؛ لذا يعتمد الاكتشاف على قراءة السمات والنداءات البرمجية لا العناصر المرئية:

import re

def detect_invisible_turnstile(html):
    """Detect invisible Turnstile on a page."""
    indicators = {
        "script_loaded": "challenges.cloudflare.com/turnstile" in html,
        "size_invisible": 'data-size="invisible"' in html or
                          "size: 'invisible'" in html or
                          'size: "invisible"' in html,
        "api_render_call": "turnstile.render" in html,
        "response_field": "cf-turnstile-response" in html,
    }

    if indicators["script_loaded"] and indicators["size_invisible"]:
        return {"mode": "invisible", "confidence": "high"}
    elif indicators["script_loaded"] and indicators["api_render_call"]:
        return {"mode": "invisible_or_programmatic", "confidence": "medium"}
    elif indicators["response_field"]:
        return {"mode": "turnstile_present", "confidence": "low"}

    return {"mode": "none", "confidence": "high"}

الأوضاع الثلاثة في جدول واحد

بعد استعراض الأوضاع الثلاثة، يجمع الجدول التالي الفروق العملية بينها جنباً إلى جنب:

الخاصية المُدار غير التفاعلي غير المرئي
هل الأداة مرئية؟ أحياناً أبداً (مؤشّر تحميل فقط) أبداً
هل يلزم عنصر حاوية؟ نعم نعم نعم (مخفي)
هل يلزم تفاعل المستخدم؟ أحياناً (مربع اختيار) لا لا
تحدّي إثبات العمل؟ نعم (قد يتصاعد) نعم (دائماً) نعم (دائماً)
بديل مربع الاختيار التفاعلي؟ نعم لا (يفشل بدلاً منه) لا (يفشل بدلاً منه)
الرمز الناتج cf-turnstile-response cf-turnstile-response cf-turnstile-response
طريقة CaptchaAI turnstile turnstile turnstile
يُوصى به لـ تسجيل الدخول والتسجيل النماذج منخفضة الاحتكاك التحقق في الخلفية

استخراج مفتاح الموقع في أي وضع

أياً كان الوضع، يبقى مفتاح الموقع (sitekey) شرطاً أساسياً للحل. تستخرجه الدالة التالية من أي وضع عبر ثلاثة أنماط بحث متتالية:

import re

def extract_turnstile_sitekey(html):
    """Extract Turnstile sitekey from page HTML (works for all modes)."""

    # Pattern 1: data-sitekey attribute in HTML
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html)
    if match:
        return match.group(1)

    # Pattern 2: JavaScript render call
    match = re.search(r"sitekey:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    # Pattern 3: Turnstile config object
    match = re.search(r"siteKey['\"]?\s*[:=]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", html)
    if match:
        return match.group(1)

    return None

حل الأوضاع الثلاثة عبر CaptchaAI

تُحلّ أوضاع Turnstile الثلاثة بالطريقة نفسها في CaptchaAI؛ فالوضع لا يؤثر في استدعاء API. أرسل مفتاح الموقع وعنوان الصفحة عبر الطريقة turnstile، ثم استطلع النتيجة حتى يجهز الرمز. في Python:

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_turnstile(sitekey, page_url):
    """Solve any Turnstile mode — managed, non-interactive, or invisible."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": sitekey,
        "pageurl": page_url,
        "json": 1,
    })

    task_id = submit.json()["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("Turnstile solve timed out")


# Use with any mode
token = solve_turnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://example.com/login")
print(f"Token: {token[:50]}...")

ونفس المنطق في Node.js:

const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveTurnstile(sitekey, pageUrl) {
  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method: "turnstile",
      sitekey,
      pageurl: pageUrl,
      json: 1,
    },
  });

  const taskId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId, json: 1 },
    });

    if (result.data.status === 1) {
      return result.data.request;
    }
  }

  throw new Error("Turnstile solve timed out");
}

// Same function works for all Turnstile modes
solveTurnstile("0x4AAAAAAAC3DHQhMMQ_Rxrg", "https://example.com/login")
  .then((token) => console.log("Token:", token.substring(0, 50)));

تذكّر: الأوضاع الثلاثة تُخرج الحقل نفسه cf-turnstile-response، فلا حاجة لتغيير منطق قراءة الرمز.

معالجة المشكلات الشائعة

إليك أكثر ما يعترض المطوّرين مع أوضاع Turnstile وطريقة معالجته:

العَرَض السبب الحل
الرمز صالح لكن النموذج يرفضه مفتاح الموقع خاطئ ويختلف عن الأداة الظاهرة تحقّق من مفتاح الموقع الذي يولّده JavaScript
الأداة غير موجودة في HTML تحميل الوضع غير المرئي بعد العرض الأولي انتظر اكتمال تحميل الصفحة وافحص استجابات XHR
عدة أدوات Turnstile في الصفحة نفسها مفاتيح مواقع مختلفة لنماذج مختلفة طابِق مفتاح الموقع مع النموذج المقصود
data-size="compact" يربك الاكتشاف «compact» متغيّر حجم لا وضع مستقل الحجم المضغوط يعمل بالوضع المُدار افتراضياً
وجود سمة data-action وسم إجراء للتحليلات لا وضع ضمّن الإجراء في الحل إن لزم للتحقق
انتهاء صلاحية الرمز قبل الإرسال تنتهي صلاحية رموز Turnstile خلال 300 ثانية احسم الحل قبل الإرسال مباشرة

الأسئلة الشائعة

هل يتغيّر مفتاح الموقع باختلاف وضع Turnstile؟

لا. مفتاح الموقع (sitekey) لا علاقة له بالوضع؛ فالموقع الواحد يستخدم المفتاح نفسه في الأوضاع الثلاثة. الوضع يتحكّم في طريقة العرض فقط، بينما يبقى المفتاح ثابتاً وهو ما تحتاجه لإرسال طلب الحل.

كم من الوقت يبقى رمز cf-turnstile-response صالحاً؟

تنتهي صلاحية رموز Turnstile خلال 300 ثانية من توليدها. لذا استدعِ الحل قبل إرسال النموذج مباشرة، لا في بداية تدفق طويل قد يستهلك المهلة قبل خطوة الإرسال.

لماذا يظهر مربع الاختيار لبعض الزوّار فقط في الوضع المُدار؟

لأن الوضع المُدار يقرّر مستوى التحدي لكل زائر اعتماداً على إشارات مثل سمعة عنوان IP وبصمة المتصفح: أصحاب الثقة العالية يمرّون بصمت، ومتوسطو الثقة يرون مربع اختيار، ومنخفضو الثقة قد يُحجبون.

هل يمكن للموقع أن يبدّل بين الأوضاع أثناء التصفح؟

نعم. تعتمد بعض المواقع الوضع المُدار افتراضياً ثم تتحوّل إلى غير التفاعلي في صفحات أو شرائح بعينها، مع بقاء مفتاح الموقع غالباً. لذلك أعِد اكتشاف الوضع مع كل انتقال بين الصفحات.

الخلاصة

أوضاع Cloudflare Turnstile الثلاثة تغيّر تجربة المستخدم فقط، لكنها تُنتج الرمز نفسه cf-turnstile-response وتُحلّ بالطريقة ذاتها. وبالنسبة للأتمتة يمكنك حلّها جميعاً باستدعاء واحد عبر خدمة حل Turnstile من CaptchaAI بمعدل نجاح مرتفع. يبقى الفارق الأهم في الاكتشاف: الوضع المُدار يترك أثراً مرئياً في HTML، بينما يتطلب غير المرئي تحليلاً أعمق للعثور على مفتاح الموقع.

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

أدلة ذات صلة

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