Phân Tích Kỹ Thuật

Hướng dẫn phát hiện triển khai cửa quay Cloudflare

Trước khi có thể giải quyết thử thách Cloudflare Turnstile, bạn cần phát hiện thử thách đó trên trang và trích xuất khóa trang web. Cloudflare Turnstile có thể được nhúng thông qua thuộc tính HTML, lệnh gọi API JavaScript hoặc được tải động sau khi hiển thị trang. Hướng dẫn này đề cập đến mọi phương pháp phát hiện — từ phân tích cú pháp HTML đơn giản đến phân tích JavaScript trong thời gian chạy.


Phương pháp thực hiện cửa quay

Các trang web nhúng Turnstile theo ba cách, mỗi cách yêu cầu một phương pháp phát hiện khác nhau:

phương pháp Nó hoạt động như thế nào Khó phát hiện
HTML ẩn <div class="cf-turnstile" data-sitekey="..."> trong nguồn trang Dễ dàng (HTML tĩnh)
Javascript rõ ràng turnstile.render() được gọi trong tập lệnh Trung bình (phân tích cú pháp JS)
Tải động Tiện ích được tải sau hành động của người dùng hoặc XHR Cứng (yêu cầu thực thi JS)

Phương pháp 1: Phát hiện HTML tĩnh

Việc tích hợp Turnstile đơn giản nhất sử dụng lớp cf-turnstile và thuộc tính data-sitekey:

import re
import requests

def detect_turnstile_html(url):
    """Detect Turnstile from static HTML."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
        "Accept": "text/html,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
    }

    response = requests.get(url, headers=headers, timeout=15)
    html = response.text

    result = {
        "turnstile_found": False,
        "sitekey": None,
        "mode": None,
        "theme": None,
        "action": None,
        "script_loaded": False,
    }

    # Check for Turnstile script
    if "challenges.cloudflare.com/turnstile" in html:
        result["script_loaded"] = True

    # Check for widget container
    if "cf-turnstile" in html:
        result["turnstile_found"] = True

        # Extract sitekey
        sitekey_match = re.search(
            r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html
        )
        if sitekey_match:
            result["sitekey"] = sitekey_match.group(1)

        # Extract mode
        if 'data-size="invisible"' in html:
            result["mode"] = "invisible"
        elif 'data-appearance="interaction-only"' in html:
            result["mode"] = "non-interactive"
        else:
            result["mode"] = "managed"

        # Extract theme
        theme_match = re.search(r'data-theme=["\'](\w+)["\']', html)
        if theme_match:
            result["theme"] = theme_match.group(1)

        # Extract action
        action_match = re.search(r'data-action=["\']([^"\']+)["\']', html)
        if action_match:
            result["action"] = action_match.group(1)

    return result

# Usage
info = detect_turnstile_html("https://staging.example.com/qa-login")
if info["turnstile_found"]:
    print(f"Sitekey: {info['sitekey']}")
    print(f"Mode: {info['mode']}")

Phương pháp 2: Phát hiện API JavaScript

Một số trang web sử dụng thuộc tính turnstile.render() thay vì HTML:

import re

def detect_turnstile_js_api(html):
    """Detect Turnstile from JavaScript render calls."""
    patterns = [
        # turnstile.render('#element', {sitekey: '...'})
        r"turnstile\.render\s*\(\s*['\"]([^'\"]+)['\"]\s*,\s*\{([^}]+)\}",
        # turnstile.render(element, {sitekey: '...'})
        r"turnstile\.render\s*\([^,]+,\s*\{([^}]+)\}",
    ]

    for pattern in patterns:
        match = re.search(pattern, html, re.DOTALL)
        if match:
            config_text = match.group(match.lastindex)

            # Extract sitekey from config object
            sitekey_match = re.search(
                r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", config_text
            )
            # Extract callback
            callback_match = re.search(
                r"callback\s*:\s*(\w+|function)", config_text
            )
            # Extract action
            action_match = re.search(
                r"action\s*:\s*['\"]([^'\"]+)['\"]", config_text
            )
            # Extract appearance
            appearance_match = re.search(
                r"appearance\s*:\s*['\"]([^'\"]+)['\"]", config_text
            )

            return {
                "found": True,
                "method": "javascript_api",
                "sitekey": sitekey_match.group(1) if sitekey_match else None,
                "callback": callback_match.group(1) if callback_match else None,
                "action": action_match.group(1) if action_match else None,
                "appearance": appearance_match.group(1) if appearance_match else None,
            }

    return {"found": False, "method": None}

Cách 3: Phát hiện tải động (Selenium/Puppeteer)

Khi Turnstile tải động sau khi tương tác với trang:

Python (Selen)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re

def detect_turnstile_dynamic(url):
    """Detect dynamically loaded Turnstile using Selenium."""
    options = webdriver.ChromeOptions()
    options.add_argument("")
    driver = webdriver.Chrome(options=options)

    try:
        driver.get(url)

        # Wait for page to fully load
        WebDriverWait(driver, 10).until(
            lambda d: d.execute_script("return document.readyState") == "complete"
        )

        result = {
            "turnstile_found": False,
            "sitekey": None,
            "iframe_present": False,
            "response_field": False,
        }

        # Check for Turnstile iframe
        iframes = driver.find_elements(By.CSS_SELECTOR, "iframe[src*='challenges.cloudflare.com']")
        if iframes:
            result["turnstile_found"] = True
            result["iframe_present"] = True

        # Check for cf-turnstile container
        containers = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
        for container in containers:
            sitekey = container.get_attribute("data-sitekey")
            if sitekey:
                result["turnstile_found"] = True
                result["sitekey"] = sitekey

        # Check for hidden response field
        response_fields = driver.find_elements(
            By.CSS_SELECTOR, "[name='cf-turnstile-response'], [name='g-recaptcha-response']"
        )
        if response_fields:
            result["response_field"] = True

        # Check page source for JS API render
        page_source = driver.page_source
        js_match = re.search(
            r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", page_source
        )
        if js_match and not result["sitekey"]:
            result["sitekey"] = js_match.group(1)
            result["turnstile_found"] = True

        return result

    finally:
        driver.quit()

Node.js (Puppeteer)

const puppeteer = require("puppeteer");

async function detectTurnstileDynamic(url) {
  const browser = await puppeteer.launch({
    headless: "new",
    args: [""],
  });

  const page = await browser.newPage();

  const result = {
    turnstileFound: false,
    sitekey: null,
    iframePresent: false,
    responseField: false,
    scriptUrl: null,
  };

  // Monitor network for Turnstile script
  page.on("response", (response) => {
    if (response.url().includes("challenges.cloudflare.com/turnstile")) {
      result.scriptUrl = response.url();
    }
  });

  await page.goto(url, { waitUntil: "networkidle2" });

  // Check for Turnstile container
  const sitekey = await page.evaluate(() => {
    const el = document.querySelector(
      ".cf-turnstile, [data-sitekey]"
    );
    return el ? el.getAttribute("data-sitekey") : null;
  });

  if (sitekey) {
    result.turnstileFound = true;
    result.sitekey = sitekey;
  }

  // Check for Turnstile iframe
  const iframes = await page.$$("iframe[src*='challenges.cloudflare.com']");
  if (iframes.length > 0) {
    result.turnstileFound = true;
    result.iframePresent = true;
  }

  // Check for response field
  const responseField = await page.$(
    "[name='cf-turnstile-response']"
  );
  result.responseField = !!responseField;

  await browser.close();
  return result;
}

detectTurnstileDynamic("https://staging.example.com/qa-login").then(console.log);

Lớp phát hiện toàn diện

import re
import requests

class TurnstileDetector:
    """Detect Cloudflare Turnstile across all implementation methods."""

    TURNSTILE_SCRIPT = "challenges.cloudflare.com/turnstile"
    SITEKEY_PATTERNS = [
        r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']',
        r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
        r"siteKey\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
        r"TURNSTILE_SITE_KEY\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
    ]

    def __init__(self, url, html=None):
        self.url = url
        self.html = html
        if not self.html:
            self._fetch()

    def _fetch(self):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                          "AppleWebKit/537.36 Chrome/120.0.0.0",
            "Accept": "text/html,*/*;q=0.8",
            "Accept-Language": "en-US,en;q=0.9",
        }
        response = requests.get(self.url, headers=headers, timeout=15)
        self.html = response.text

    def detect(self):
        """Run all detection methods and return results."""
        return {
            "url": self.url,
            "turnstile_present": self.has_turnstile(),
            "sitekey": self.extract_sitekey(),
            "mode": self.detect_mode(),
            "implementation": self.detect_implementation(),
            "script_loaded": self.has_script(),
            "response_field": self.has_response_field(),
            "action": self.extract_action(),
            "theme": self.extract_theme(),
        }

    def has_turnstile(self):
        return (
            self.has_script()
            or "cf-turnstile" in self.html
            or self.extract_sitekey() is not None
        )

    def has_script(self):
        return self.TURNSTILE_SCRIPT in self.html

    def has_response_field(self):
        return "cf-turnstile-response" in self.html

    def extract_sitekey(self):
        for pattern in self.SITEKEY_PATTERNS:
            match = re.search(pattern, self.html)
            if match:
                return match.group(1)
        return None

    def detect_mode(self):
        if 'data-size="invisible"' in self.html or "size: 'invisible'" in self.html:
            return "invisible"
        if 'data-appearance="interaction-only"' in self.html:
            return "non-interactive"
        if "cf-turnstile" in self.html:
            return "managed"
        return "unknown"

    def detect_implementation(self):
        if "cf-turnstile" in self.html and re.search(r"data-sitekey=", self.html):
            return "html_implicit"
        if "turnstile.render" in self.html:
            return "javascript_explicit"
        if self.has_script() and not "cf-turnstile" in self.html:
            return "dynamic_loading"
        return "unknown"

    def extract_action(self):
        match = re.search(r'data-action=["\']([^"\']+)["\']', self.html)
        if match:
            return match.group(1)
        match = re.search(r"action\s*:\s*['\"]([^'\"]+)['\"]", self.html)
        return match.group(1) if match else None

    def extract_theme(self):
        match = re.search(r'data-theme=["\'](\w+)["\']', self.html)
        return match.group(1) if match else "auto"

# Usage
detector = TurnstileDetector("https://staging.example.com/qa-login")
info = detector.detect()

if info["turnstile_present"]:
    print(f"Sitekey: {info['sitekey']}")
    print(f"Mode: {info['mode']}")
    print(f"Implementation: {info['implementation']}")

Giải quyết sau khi phát hiện

Sau khi phát hiện, giải quyết bằng CaptchaAI:

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_detected_turnstile(detection_result):
    """Solve Turnstile using detection results."""
    if not detection_result["turnstile_present"]:
        raise ValueError("No Turnstile detected")

    if not detection_result["sitekey"]:
        raise ValueError("Sitekey not found — may need browser-based extraction")

    params = {
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": detection_result["sitekey"],
        "pageurl": detection_result["url"],
        "json": 1,
    }

    # Include action if present
    if detection_result.get("action"):
        params["action"] = detection_result["action"]

    submit = requests.post("https://ocr.captchaai.com/in.php", data=params)
    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")

# Full workflow
detector = TurnstileDetector("https://example.com/signup")
info = detector.detect()

if info["turnstile_present"]:
    token = solve_detected_turnstile(info)
    print(f"Token: {token[:50]}...")

Vỏ cạnh

Kịch bản Thử thách Giải pháp
Sitekey trong tệp JS bên ngoài Không có trong trang HTML Phân tích các tệp JavaScript được liên kết cho các mẫu khóa trang web
Khóa trang web từ phản hồi API Đã tải sau cuộc gọi XHR Giám sát các yêu cầu mạng về khóa trang web trong phản hồi JSON
Nhiều vật dụng quay vòng Các khóa trang web khác nhau trên cùng một trang Khớp khóa trang web với biểu mẫu cụ thể mà bạn đang gửi
Cloudflare Turnstile trong bóng tối DOM Không thể truy cập thông qua bộ chọn thông thường Sử dụng shadowRoot.querySelector trong ngữ cảnh trình duyệt
Khóa trang web được hiển thị phía máy chủ Được nhúng trong các biến mẫu Kiểm tra thẻ <script> cho các đối tượng cấu hình
Cloudflare Turnstile phía sau xác thực Không hiển thị trên trang công khai Xác thực trước, sau đó phát hiện

Khắc phục sự cố

Triệu chứng nguyên nhân sửa chữa
Đã tìm thấy thẻ tập lệnh nhưng không có khóa trang web Kết xuất API JS với cấu hình từ một nguồn khác Kiểm tra tất cả các tệp JS được liên kết và phản hồi XHR
Đã trích xuất khóa trang web sai Nhiều tiện ích CAPTCHA trên trang Khớp khóa trang web với các phần tử biểu mẫu xung quanh
Phát hiện hoạt động nhưng giải quyết không thành công Tham số hành động cần thiết để xác thực Bao gồm giá trị data-action trong yêu cầu giải quyết
Tiện ích không có trong HTML ban đầu Tải động sau khi tương tác với người dùng Sử dụng Selenium/Puppeteer để hiển thị toàn trang
Trường cf-turnstile-response trống Widget vẫn chưa hoàn thành Đợi widget tải xong

Câu hỏi thường gặp

Khóa trang web Turnstile có thể thay đổi không?

Vâng. Người điều hành trang web có thể xoay vòng khóa trang web bất kỳ lúc nào. Luôn trích xuất khóa trang web mới từ trang thay vì mã hóa cứng nó.

Tôi có cần tham số hành động không?

Chỉ khi trang web xác thực phía máy chủ. Nếu data-action có trong HTML, hãy đưa nó vào yêu cầu giải quyết của bạn để có kết quả tốt nhất.

Nếu tôi không thể tìm thấy mã khóa trang web thì sao?

Khóa trang web có thể ở trong tệp JavaScript bên ngoài, phản hồi API hoặc được tạo động. Sử dụng DevTools của trình duyệt (tab Mạng) để tìm nó hoặc sử dụng Selenium/Puppeteer để giải nén nó sau khi hiển thị toàn bộ trang.

Phương pháp phát hiện có ảnh hưởng đến việc giải quyết không?

Không. Bộ giải Turnstile của CaptchaAI hoạt động giống nhau bất kể tiện ích được triển khai như thế nào. Bạn chỉ cần mã trang web và URL trang.


Tóm tắt

Việc phát hiện Cloudflare Turnstile yêu cầu kiểm tra thẻ tập lệnh Turnstile, vùng chứa cf-turnstile, thuộc tính data-sitekey và lệnh gọi turnstile.render(). Sử dụng phân tích cú pháp HTML tĩnh để tích hợp đơn giản và Selenium/Puppeteer cho các tiện ích được tải động. Sau khi phát hiện, giải quyết bằngBộ giải cửa quay của CaptchaAIbằng cách sử dụng khóa trang web được trích xuất - tất cả các chế độ đều được xử lý giống hệt nhau với tỷ lệ thành công 100%.

bài viết liên quan

Os comentários estão desativados para este artigo.