Integrations

Integração do Vault para gerenciamento de chaves de API CaptchaAI

Se a chave de API da CaptchaAI está em um .env versionado por engano ou colada no canal do time, o conserto não é trocar a chave: é tirar o segredo do código. Com o HashiCorp Vault ela fica cifrada em um caminho KV, cada worker a lê em tempo de execução com identidade própria e toda leitura entra na trilha de auditoria. Nenhuma linha do seu código de resolução precisa saber qual é a chave.

São quatro passos:

  1. Gravar a chave em um caminho KV v2
  2. Escrever uma política de leitura restrita aos workers
  3. Ler o segredo em tempo de execução no Python
  4. Repetir o padrão em Node.js, sem SDK

Antes de começar

  • Servidor HashiCorp Vault (auto-hospedado ou HCP Vault)
  • Acesso ao Vault por CLI ou API
  • Uma chave de API da CaptchaAI ativa (qualquer plano serve — o BASIC custa US$ 15/mês e inclui 5 threads)
  • Python 3.8+ ou Node.js 18+

Um servidor em modo dev já basta para percorrer o guia; a sequência em produção é a mesma.

O que o Vault resolve no fluxo de resolução de CAPTCHA

Sem Vault Com Vault
Chave de API no arquivo .env ou no código Chave cifrada em um caminho KV do Vault
Chave compartilhada por Slack ou e-mail Leitura por API autenticada
Nenhuma trilha de auditoria Cada leitura registrada com identidade
Rotação manual da chave Rotação sem novo deploy
Mesma chave em todos os ambientes Uma chave por ambiente, com políticas distintas

Há também conformidade: quando o pipeline processa dados pessoais, a LGPD (RGPD em Portugal) espera controle de acesso demonstrável. "Quem leu a credencial de produção em 12 de março?" o Vault responde com um registro; o .env, não.

Passo 1: guarde a chave no caminho KV

O motor KV v2 mantém versões do segredo, o que facilita voltar atrás se uma rotação der errado.

# Enable the KV secrets engine (if not already enabled)
vault secrets enable -path=secret kv-v2

# Store the CaptchaAI API key
vault kv put secret/captchaai api_key="YOUR_API_KEY"

# Verify
vault kv get secret/captchaai

Passo 2: escreva a política de leitura

Os workers de resolução não precisam gravar nada. Dê a eles apenas leitura, no caminho exato — nada de curingas:

# captcha-worker-policy.hcl
path "secret/data/captchaai" {
  capabilities = ["read"]
}

path "secret/metadata/captchaai" {
  capabilities = ["read"]
}

Aplique a política:

vault policy write captcha-worker captcha-worker-policy.hcl

No KV v2 o dado vive em secret/data/... e os metadados em secret/metadata/.... Esquecer o segundo caminho é a causa mais comum de 403 Forbidden inexplicável.

Passo 3: leia o segredo em Python

O cliente hvac busca a chave uma vez, mantém em memória e renova de hora em hora. Assim o Vault não vira ponto de falha a cada resolução: se cair por alguns minutos, o worker segue com a chave em cache.

# vault_solver.py
import os
import time
import hvac
import requests

# Connect to Vault
vault_client = hvac.Client(
    url=os.environ.get("VAULT_ADDR", "http://127.0.0.1:8200"),
    token=os.environ.get("VAULT_TOKEN"),
)

def get_api_key():
    """Retrieve CaptchaAI API key from Vault."""
    secret = vault_client.secrets.kv.v2.read_secret_version(
        path="captchaai",
        mount_point="secret",
    )
    return secret["data"]["data"]["api_key"]

class CaptchaSolver:
    """CAPTCHA solver with Vault-managed credentials."""

    def __init__(self):
        self.api_key = get_api_key()
        self.session = requests.Session()
        self._key_fetched_at = time.time()
        self._key_refresh_interval = 3600  # Re-fetch key hourly

    def _refresh_key_if_needed(self):
        """Periodically refresh the key from Vault."""
        if time.time() - self._key_fetched_at > self._key_refresh_interval:
            self.api_key = get_api_key()
            self._key_fetched_at = time.time()

    def solve(self, sitekey, pageurl):
        """Solve reCAPTCHA v2 using Vault-managed key."""
        self._refresh_key_if_needed()

        # Submit
        resp = self.session.get("https://ocr.captchaai.com/in.php", params={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": "1",
        })
        result = resp.json()

        if result.get("status") != 1:
            raise Exception(f"Submit failed: {result.get('request')}")

        task_id = result["request"]
        time.sleep(15)

        for _ in range(25):
            poll = self.session.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": "1",
            })
            poll_result = poll.json()

            if poll_result.get("status") == 1:
                return poll_result["request"]
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                raise Exception(f"Error: {poll_result.get('request')}")

            time.sleep(5)

        raise Exception("Timeout")

# Usage
solver = CaptchaSolver()
token = solver.solve(
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    "https://www.google.com/recaptcha/api2/demo"
)
print(f"Token: {token[:30]}...")

Passo 4: o mesmo padrão em Node.js

A API HTTP do Vault dispensa SDK: um GET no caminho data com o cabeçalho X-Vault-Token devolve o segredo. O ciclo é idêntico ao do Python — in.php envia a tarefa, res.php consulta até o token chegar.

// vault_solver.js
const axios = require('axios');

const VAULT_ADDR = process.env.VAULT_ADDR || 'http://127.0.0.1:8200';
const VAULT_TOKEN = process.env.VAULT_TOKEN;

async function getApiKey() {
  const resp = await axios.get(
    `${VAULT_ADDR}/v1/secret/data/captchaai`,
    { headers: { 'X-Vault-Token': VAULT_TOKEN } }
  );
  return resp.data.data.data.api_key;
}

class CaptchaSolver {
  constructor() {
    this.apiKey = null;
    this.keyFetchedAt = 0;
    this.refreshInterval = 3600000; // 1 hour
  }

  async init() {
    this.apiKey = await getApiKey();
    this.keyFetchedAt = Date.now();
  }

  async refreshKeyIfNeeded() {
    if (Date.now() - this.keyFetchedAt > this.refreshInterval) {
      this.apiKey = await getApiKey();
      this.keyFetchedAt = Date.now();
    }
  }

  async solve(sitekey, pageurl) {
    await this.refreshKeyIfNeeded();

    const submit = await axios.get('https://ocr.captchaai.com/in.php', {
      params: {
        key: this.apiKey, method: 'userrecaptcha',
        googlekey: sitekey, pageurl, json: '1',
      },
    });

    if (submit.data.status !== 1) throw new Error(submit.data.request);
    const taskId = submit.data.request;

    await new Promise(r => setTimeout(r, 15000));

    for (let i = 0; i < 25; i++) {
      const poll = await axios.get('https://ocr.captchaai.com/res.php', {
        params: { key: this.apiKey, action: 'get', id: taskId, json: '1' },
      });

      if (poll.data.status === 1) return poll.data.request;
      if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
      await new Promise(r => setTimeout(r, 5000));
    }
    throw new Error('Timeout');
  }
}

(async () => {
  const solver = new CaptchaSolver();
  await solver.init();

  const token = await solver.solve(
    '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
    'https://www.google.com/recaptcha/api2/demo'
  );
  console.log(`Token: ${token.slice(0, 30)}...`);
})();

Rotação da chave sem novo deploy

  1. Gere uma nova chave de API no painel da CaptchaAI
  2. Atualize o Vault: vault kv put secret/captchaai api_key="NEW_KEY"
  3. Os workers pegam a nova chave no próximo ciclo de atualização (uma hora, no exemplo acima)
  4. Revogue a chave antiga no painel depois que todos os workers tiverem atualizado

Nenhuma alteração de código, nenhum deploy. Para encurtar a janela em que as duas chaves convivem, reduza _key_refresh_interval: 300 s é confortável para quem gira credenciais com frequência.

Como autenticar cada tipo de worker

Token estático resolve no notebook; em produção ele vira dívida. Duas perguntas guiam a escolha:

  • O worker roda em plataforma que já emite identidade (Kubernetes, EC2, Lambda)? Use essa identidade.
  • Alguém copia o segredo à mão em algum momento? Então ainda há um ponto manual a eliminar.
Método Indicado para Configuração
Token Desenvolvimento, CI/CD Variável de ambiente VAULT_TOKEN
AppRole Serviços de produção role ID + secret ID
Kubernetes Cargas em K8s JWT da service account
AWS IAM Workers em EC2/Lambda Role da instância

AppRole: o padrão recomendado em produção

Um cenário concreto: um time de QA roda os workers em sa-east-1 (São Paulo) para reduzir o RTT e mantém o Vault em outra conta. Com AppRole, o role ID vai na imagem do container e o secret ID é entregue pelo orquestrador com TTL curto — nenhum token de longa duração cruza a rede.

# AppRole authentication — no static token needed
vault_client = hvac.Client(url=os.environ["VAULT_ADDR"])
vault_client.auth.approle.login(
    role_id=os.environ["VAULT_ROLE_ID"],
    secret_id=os.environ["VAULT_SECRET_ID"],
)

# Now read the secret
secret = vault_client.secrets.kv.v2.read_secret_version(path="captchaai")
api_key = secret["data"]["data"]["api_key"]

Quando algo dá errado

Problema Causa provável Correção
403 Forbidden do Vault A política não cobre o caminho lido Confira secret/data/... e secret/metadata/... em captcha-worker-policy.hcl
VAULT_TOKEN expirado TTL do token estourou Migre para AppRole, com renovação automática
A chave não atualiza Intervalo de atualização longo demais Reduza _key_refresh_interval
Vault indisponível Rede ou servidor fora do ar Mantenha a chave em cache na memória como plano B
ERROR_WRONG_USER_KEY da CaptchaAI O valor gravado no Vault veio com espaço ou quebra de linha Regrave com vault kv put e confira com vault kv get

Perguntas frequentes

Vale a pena usar o Vault se eu tenho só um worker?

Vale mais pela rotação do que pela criptografia: mesmo com um worker só, você troca a chave sem republicar o serviço. Em script pessoal, uma variável de ambiente resolve — veja como proteger credenciais em variáveis de ambiente.

Cada leitura do Vault atrasa a resolução do CAPTCHA?

Não, porque a leitura não acontece a cada resolução. O cliente guarda a chave em memória até o intervalo vencer: uma requisição por hora e por processo, irrelevante diante do tempo de resolução.

Como isolo produção de staging?

Use caminhos separados — secret/captchaai/dev, secret/captchaai/staging, secret/captchaai/prod — e uma política por ambiente. Use também chaves de API distintas: isso separa o consumo de threads.

Dá para trocar o Vault pelo AWS Secrets Manager?

Dá, e o desenho não muda: leitura em tempo de execução com boto3, cache em memória e rotação sem deploy. Você perde a política declarativa em HCL e ganha uma peça a menos para operar.

O Vault muda alguma coisa nos tipos de CAPTCHA suportados?

Nada — ele cuida só da credencial. Os tipos seguem os mesmos: reCAPTCHA v2 e v3 (incluindo Enterprise), Cloudflare Turnstile e Cloudflare Challenge, GeeTest v3, CAPTCHAs de imagem/OCR e de grade, além de CaptchaFox (beta), Friendly Captcha (beta) e Lemin (beta). hCaptcha e FunCaptcha não são suportados; o GeeTest v4 está anunciado como "em breve".

Artigos relacionados

Próximas etapas

Comece pequeno: mova uma chave de staging para o Vault, valide a leitura pelo worker e só depois toque em produção. Crie sua chave de API e faça a primeira leitura hoje.

Guias relacionados:

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