Análises Técnicas

Guia de extração de URL reCAPTCHA Anchor e Bframe

O reCAPTCHA é renderizado dentro de dois iframes aninhados: o iframe anchor (contendo o widget de caixa de seleção) e o iframe bframe (contendo o desafio da imagem). Esses iframes contêm parâmetros codificados que alguns fluxos de trabalho de automação avançados precisam extrair. Este guia explica a arquitetura iframe, o formato dos parâmetros e as técnicas de extração.


Arquitetura iframe reCAPTCHA

Target page (staging.example.com/qa-login)
    └── <iframe src="https://www.google.com/recaptcha/api2/anchor?...">
        │   ← Anchor iframe: "I'm not a robot" checkbox
        │
        └── <iframe src="https://www.google.com/recaptcha/api2/bframe?...">
                ← Bframe iframe: Image challenge grid (loads when clicked)

Iframe âncora

O iframe âncora contém o widget de caixa de seleção e a análise de risco inicial:

https://www.google.com/recaptcha/api2/anchor?
    ar=1
    &k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp  ← site key
    &co=aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM.           ← encoded origin
    &hl=en                                           ← language
    &v=jF2Zb_rr_5sv8dMHoGIn-XxY                    ← reCAPTCHA version
    &size=normal                                     ← widget size
    &cb=89fu2pf0swif                                ← callback ID

Bframe iframe

O iframe bframe contém o desafio da imagem (carregado apenas quando o clique na caixa de seleção aciona um desafio):

https://www.google.com/recaptcha/api2/bframe?
    hl=en
    &v=jF2Zb_rr_5sv8dMHoGIn-XxY
    &k=6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp

Parâmetros de URL âncora

Parâmetro Nome Descrição
k Chave do site A chave do site reCAPTCHA
co Origem codificada Origem codificada em Base64 (protocolo + domínio + porta)
v Versão Hash da versão do pacote JavaScript reCAPTCHA
hl Idioma Código do idioma do desafio
size Tamanho normal, compact ou invisible
cb Retorno de chamada Identificador exclusivo da função de retorno de chamada
theme Tema light ou dark
ar Proporção de aspecto Exibir sinalizador de proporção

Decodificando o parâmetro co

O parâmetro co contém a origem codificada em base64:

import base64

co_value = "aHR0cHM6Ly9leGFtcGxlLmNvbTo0NDM."
# Remove trailing period (padding artifact)
decoded = base64.b64decode(co_value.rstrip(".") + "==").decode()
print(decoded)  # "https://example.com:443"

Isso revela o domínio de origem para o qual o reCAPTCHA foi configurado.


Extraindo URLs âncora e bframe

Extração Python da fonte da página

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, parse_qs
import re
import base64

def extract_recaptcha_iframes(url):
    """Extract reCAPTCHA anchor and bframe iframe URLs and parameters."""
    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",
    }

    response = requests.get(url, headers=headers, timeout=15)
    soup = BeautifulSoup(response.text, "html.parser")

    result = {
        "anchor_url": None,
        "bframe_url": None,
        "site_key": None,
        "origin": None,
        "version": None,
        "language": None,
    }

    # Find anchor iframe
    anchor_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*anchor"))
    if anchor_iframe:
        anchor_url = anchor_iframe.get("src", "")
        result["anchor_url"] = anchor_url

        # Parse parameters
        parsed = urlparse(anchor_url)
        params = parse_qs(parsed.query)

        result["site_key"] = params.get("k", [None])[0]
        result["version"] = params.get("v", [None])[0]
        result["language"] = params.get("hl", [None])[0]

        # Decode origin
        co = params.get("co", [None])[0]
        if co:
            try:
                padded = co.rstrip(".") + "=="
                result["origin"] = base64.b64decode(padded).decode()
            except Exception:
                result["origin"] = co

    # Find bframe iframe (may not be in source — loaded dynamically)
    bframe_iframe = soup.find("iframe", src=re.compile(r"recaptcha.*bframe"))
    if bframe_iframe:
        result["bframe_url"] = bframe_iframe.get("src", "")

    # Construct bframe URL from anchor parameters if not found
    if not result["bframe_url"] and result["site_key"] and result["version"]:
        result["bframe_url"] = (
            f"https://www.google.com/recaptcha/api2/bframe?"
            f"hl={result['language'] or 'en'}"
            f"&v={result['version']}"
            f"&k={result['site_key']}"
        )

    return result

iframes = extract_recaptcha_iframes("https://staging.example.com/qa-login")
print(f"Site key: {iframes['site_key']}")
print(f"Origin: {iframes['origin']}")
print(f"Anchor URL: {iframes['anchor_url']}")

Extração de Node.js

const axios = require("axios");
const cheerio = require("cheerio");
const { URL } = require("url");

async function extractRecaptchaIframes(pageUrl) {
    const { data: html } = await axios.get(pageUrl, {
        headers: {
            "User-Agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
                "AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36",
        },
        timeout: 15000,
    });

    const $ = cheerio.load(html);
    const result = {
        anchorUrl: null,
        bframeUrl: null,
        siteKey: null,
        origin: null,
        version: null,
    };

    // Find anchor iframe
    const anchorIframe = $("iframe[src*='recaptcha'][src*='anchor']");
    if (anchorIframe.length) {
        const src = anchorIframe.attr("src");
        result.anchorUrl = src;

        const url = new URL(src);
        result.siteKey = url.searchParams.get("k");
        result.version = url.searchParams.get("v");

        // Decode origin
        const co = url.searchParams.get("co");
        if (co) {
            try {
                result.origin = Buffer.from(
                    co.replace(/\.$/, ""), "base64"
                ).toString();
            } catch {}
        }
    }

    // Construct bframe URL
    if (result.siteKey && result.version) {
        result.bframeUrl =
            `https://www.google.com/recaptcha/api2/bframe?` +
            `hl=en&v=${result.version}&k=${result.siteKey}`;
    }

    return result;
}

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

Extração de selênio (páginas dinâmicas)

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

def extract_iframes_selenium(url):
    """Extract reCAPTCHA iframe URLs from a dynamically loaded page."""
    driver = webdriver.Chrome()
    driver.get(url)
    time.sleep(3)  # Wait for reCAPTCHA to load

    result = {"anchor_url": None, "bframe_url": None}

    # Find all iframes
    iframes = driver.find_elements(By.TAG_NAME, "iframe")

    for iframe in iframes:
        src = iframe.get_attribute("src") or ""
        if "recaptcha" in src and "anchor" in src:
            result["anchor_url"] = src
        elif "recaptcha" in src and "bframe" in src:
            result["bframe_url"] = src

    driver.quit()
    return result

Quando você precisa de URLs âncora/bframe

A maioria dos fluxos de trabalho de automação NÃO precisa de URLs âncora ou bframe. A API CaptchaAI padrão requer apenas sitekey e pageurl. No entanto, URLs âncora/bframe são úteis para:

1. Verificando a chave do site correta

Quando uma página tem várias instâncias do reCAPTCHA ou a chave do site é carregada dinamicamente:

# Extract sitekey from anchor URL when it's not in the page HTML
iframes = extract_recaptcha_iframes(url)
sitekey = iframes["site_key"]  # Guaranteed correct from iframe URL

2. Determinando a versão do reCAPTCHA

# The anchor URL reveals the exact reCAPTCHA version
if "/api2/anchor" in anchor_url:
    recaptcha_type = "v2"
elif "/enterprise/anchor" in anchor_url:
    recaptcha_type = "enterprise"

3. Correspondência da origem para implementações restritas ao domínio

# Decode the origin from the co parameter
origin = decode_co_parameter(iframes["co"])
# Use this origin as the pageurl for the solver

4. Depuração resolve falhas

Quando os tokens são rejeitados, comparar os parâmetros do URL âncora com a solicitação do solucionador pode revelar incompatibilidades:

def debug_solve_params(anchor_url, solver_pageurl, solver_sitekey):
    """Compare anchor params with solver request to find mismatches."""
    parsed = urlparse(anchor_url)
    params = parse_qs(parsed.query)

    issues = []

    # Check sitekey
    anchor_key = params.get("k", [None])[0]
    if anchor_key != solver_sitekey:
        issues.append(f"Sitekey mismatch: anchor={anchor_key}, solver={solver_sitekey}")

    # Check origin
    co = params.get("co", [None])[0]
    if co:
        origin = base64.b64decode(co.rstrip(".") + "==").decode()
        solver_parsed = urlparse(solver_pageurl)
        solver_origin = f"{solver_parsed.scheme}://{solver_parsed.netloc}"
        if origin != solver_origin:
            issues.append(f"Origin mismatch: anchor={origin}, solver={solver_origin}")

    return issues if issues else ["No mismatches found"]

Resolução padrão (recomendado)

Para a maioria dos casos de uso, ignore a extração de iframe e resolva diretamente com CaptchaAI:

import requests
import time

API_KEY = "YOUR_API_KEY"

# All you need: sitekey + pageurl
submit = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
    "pageurl": "https://staging.example.com/qa-login",
    "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:
        token = result["request"]
        print(f"Token: {token[:50]}...")
        break

A extração Anchor/bframe só é necessária quando a solução padrão falha devido a problemas de chave de site ou domínio que exigem uma investigação mais profunda.


Perguntas frequentes

Preciso fornecer URLs âncora ou bframe para CaptchaAI?

Não. CaptchaAI precisa apenas de sitekey e pageurl. O solucionador lida internamente com a interação âncora e bframe. A extração desses URLs é útil para depuração, mas não é necessária para solução.

Por que o iframe bframe não aparece na origem da página?

O iframe bframe é criado dinamicamente pelo JavaScript do reCAPTCHA quando o usuário clica na caixa de seleção e um desafio é acionado. Não está presente no HTML inicial. Você precisa do Selenium ou do Puppeteer para interagir com o widget e capturar a URL do bframe.

Qual é o parâmetro v no URL âncora?

O parâmetro v é o hash da versão do pacote JavaScript reCAPTCHA. Ele muda periodicamente quando o Google atualiza o reCAPTCHA. Não é necessário para resolver — CaptchaAI lida com diferenças de versão automaticamente.

O parâmetro co pode ajudar a depurar problemas de domínio?

Sim. O parâmetro co é a origem codificada em base64 (protocolo + domínio + porta). A decodificação revela exatamente em qual domínio o reCAPTCHA pensa que está sendo executado. Se isso não corresponder ao domínio para o qual você está enviando o token, você descobriu que a incompatibilidade de domínio está causando a rejeição do token.


Resumo

O reCAPTCHA usa dois iframes: âncora (widget de caixa de seleção) e bframe (desafio de imagem). O URL âncora contém a chave do site, a origem codificada e o hash da versão. Para resolução padrão comCaptchaAI, você só precisa de sitekey e pageurl — não é necessária extração de URL de iframe. Use técnicas de extração de iframe para depurar falhas de verificação de domínio ou extrair chaves de site de páginas carregadas dinamicamente.

Artigos relacionados

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