Tutoriales de API

Lista blanca de IP de CaptchaAI y seguridad de clave API

Una clave API de CaptchaAI expuesta se agota sola: basta con que aparezca un instante en un repositorio público de GitHub para que los bots la recojan y consuman tu saldo antes de que te enteres. Blindarla no requiere nada exótico, solo tres decisiones que se aplican una vez y protegen a partir de entonces:

  • Sácala del código y cárgala desde variables de entorno.
  • Restringe desde qué IPs puede usarse, con una lista blanca.
  • Rótala con un plan claro, no solo cuando algo ya ha salido mal.

Esta guía recorre las tres con código listo para copiar, más la parte que casi todo el mundo olvida — no filtrar la clave en los logs ni en tu pipeline de CI/CD.

Lista blanca de IP: restringe desde dónde se usa la clave

Empecemos por la medida que da nombre a esta guía, porque es la que sigue protegiéndote aunque la clave se filtre. La idea es sencilla: autorizas solo las IPs de tus propios servidores, de modo que una clave copiada desde otra red no sirva para nada.

  • Revisa en tu panel de CaptchaAI si ofrece opciones de restricción por IP.
  • Si están disponibles, deja únicamente las IPs de salida de tu infraestructura.
  • Con IP dinámica, enruta el tráfico de tus workers por un gateway o un NAT con IP fija, en lugar de abrir un rango entero.

Un caso habitual: una agencia en Madrid o Ciudad de México que automatiza flujos para varios clientes. Si todos comparten una misma clave, un .env filtrado en un proyecto compromete el saldo de todos. Con una clave por cliente y por entorno —desarrollo, staging y producción— el radio de impacto queda acotado a un único proyecto. Y como el saldo de CaptchaAI se factura en USD por threads, es dinero real que conviene aislar.

Por dónde se escapa una clave API

Antes de blindar nada, conviene saber por dónde se filtran las claves en la práctica. Casi siempre es una de estas cuatro rutas, y el resultado en todas es el mismo: alguien más termina gastando tu saldo.

Exposed API key:
  ├── Leaked in Git repository
  ├── Hardcoded in client-side code
  ├── Shared in documentation
  └── Visible in logs

Impact:
  ├── Balance drained by unauthorized users
  ├── Usage spikes from abuse
  └── Key disabled by service provider

Guarda la clave fuera del código fuente

El almacenamiento seguro se apoya en tres piezas que trabajan juntas:

  • La clave se lee de una variable de entorno, nunca de una constante en el código.
  • El valor real vive en un archivo .env local que no se versiona.
  • .gitignore mantiene ese .env fuera del repositorio.

Nunca escribas la clave en el código

La regla número uno: la clave no vive en el código fuente. Cárgala desde una variable de entorno o desde un archivo .env que jamás llegue a Git.

# BAD — key in source code
API_KEY = "abc123def456"  # DO NOT DO THIS

# GOOD — environment variable
import os
API_KEY = os.environ["CAPTCHAAI_API_KEY"]

# GOOD — .env file (not committed to Git)
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["CAPTCHAAI_API_KEY"]

Archivo .env

Un archivo .env mantiene la clave fuera del repositorio, siempre que lo excluyas del control de versiones:

# .env (add to .gitignore!)
CAPTCHAAI_API_KEY=your_api_key_here

.gitignore

Añade el patrón a .gitignore antes del primer commit, nunca después de haberlo subido:

# Always ignore .env files
.env
.env.local
.env.production

Carga la configuración desde el entorno

Centraliza la lectura de la clave en una clase de configuración: que falle de inmediato si la variable no está definida y que valide la clave contra el endpoint res.php antes de empezar a trabajar.

import os


class CaptchaConfig:
    """Load CaptchaAI config from environment."""

    def __init__(self):
        self.api_key = os.environ.get("CAPTCHAAI_API_KEY")
        if not self.api_key:
            raise EnvironmentError(
                "CAPTCHAAI_API_KEY not set. "
                "Set it in your environment or .env file."
            )
        self.base_url = os.environ.get(
            "CAPTCHAAI_URL", "https://ocr.captchaai.com"
        )

    def validate(self):
        """Verify the API key works."""
        import requests
        resp = requests.get(f"{self.base_url}/res.php", params={
            "key": self.api_key,
            "action": "getbalance",
            "json": 1,
        }, timeout=10)
        data = resp.json()
        if data.get("status") != 1:
            raise RuntimeError(f"Invalid API key: {data.get('request')}")
        return float(data["request"])


# Usage
config = CaptchaConfig()
balance = config.validate()
print(f"Key valid, balance: ${balance:.2f}")

Rota la clave cada cierto tiempo

Rotar la clave de forma periódica limita el daño si alguna vez se filtra. Mantén una clave secundaria lista y cambia a ella en cuanto la primaria deje de responder:

import os
import datetime


class KeyManager:
    """Manage API key rotation."""

    def __init__(self):
        self.primary_key = os.environ.get("CAPTCHAAI_API_KEY")
        self.secondary_key = os.environ.get("CAPTCHAAI_API_KEY_BACKUP")
        self.active_key = self.primary_key

    def get_key(self):
        return self.active_key

    def rotate(self):
        """Switch to secondary key."""
        if self.secondary_key:
            self.active_key = self.secondary_key
            print("Rotated to secondary key")
        else:
            print("No secondary key configured")

    def test_key(self, key):
        """Verify a key is valid."""
        import requests
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": key, "action": "getbalance", "json": 1,
        }, timeout=10)
        return resp.json().get("status") == 1


# Usage
keys = KeyManager()

# If primary fails, rotate to secondary
if not keys.test_key(keys.get_key()):
    keys.rotate()

Como referencia práctica, conviene rotar en estos momentos:

  • De forma programada, cada 90 días.
  • Cada vez que alguien con acceso al panel deja el equipo.
  • Ante cualquier sospecha de filtración, sin esperar a confirmarla.

Valida las solicitudes antes de enviarlas

Comprobar las entradas antes de llamar a in.php evita errores tontos que terminan exponiendo la clave — por ejemplo, registrar una URL con la clave pegada o enviar un method inexistente:

import requests
import logging

logger = logging.getLogger(__name__)


class SecureSolver:
    """Solver with security best practices."""

    def __init__(self, api_key):
        self.api_key = api_key
        self.base = "https://ocr.captchaai.com"

    def solve(self, method, **params):
        # Validate inputs
        self._validate_params(method, params)

        data = {"key": self.api_key, "method": method, "json": 1}
        data.update(params)

        # Log without exposing key
        logger.info(
            "Submitting %s solve for %s",
            method, params.get("pageurl", "unknown"),
        )

        resp = requests.post(
            f"{self.base}/in.php", data=data, timeout=30,
        )
        return resp.json()

    def _validate_params(self, method, params):
        """Prevent common security mistakes."""
        # Ensure pageurl is a valid URL
        pageurl = params.get("pageurl", "")
        if pageurl and not pageurl.startswith(("http://", "https://")):
            raise ValueError(f"Invalid pageurl: {pageurl}")

        # Ensure method is valid
        valid_methods = {
            "userrecaptcha", "turnstile", "geetest",
            "base64", "post", "bls", "cloudflare_challenge",
        }
        if method not in valid_methods:
            raise ValueError(f"Unknown method: {method}")

Registro de logs sin exponer la clave

El punto más olvidado son los logs. Una clave de 32 caracteres impresa en un log de producción es tan peligrosa como una filtrada en Git, y casi nadie la busca ahí. Redáctala automáticamente con un formateador:

import logging
import re

logger = logging.getLogger(__name__)


class SafeFormatter(logging.Formatter):
    """Redact API keys from log messages."""

    KEY_PATTERN = re.compile(r'[a-f0-9]{32}', re.IGNORECASE)

    def format(self, record):
        msg = super().format(record)
        return self.KEY_PATTERN.sub("[REDACTED]", msg)


# Configure safe logging
handler = logging.StreamHandler()
handler.setFormatter(SafeFormatter("%(levelname)s: %(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Key is automatically redacted in logs
logger.info(f"Using key: abc123def456ghi789jkl012mno345pq")
# Output: INFO: Using key: [REDACTED]

Secrets en Docker

En despliegues con contenedores, nunca incrustes la clave dentro de la imagen. Pásala por variable de entorno o, mejor todavía, con Docker secrets:

# Dockerfile — DO NOT embed keys here
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install requests
CMD ["python", "solver.py"]
# docker-compose.yml
services:
  solver:
    build: .
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
    # Or use Docker secrets:
    secrets:

      - captchaai_key

secrets:
  captchaai_key:
    file: ./secrets/captchaai_key.txt

Seguridad en CI/CD

GitHub Actions

En integración continua, la clave vive en el gestor de secrets de la plataforma, nunca escrita en el YAML del workflow:

# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@v4
      - name: Run tests
        env:
          CAPTCHAAI_API_KEY: ${{ secrets.CAPTCHAAI_API_KEY }}
        run: python test_solver.py

Nunca imprimas ni hagas echo del secreto en la salida de CI: los logs de las ejecuciones suelen ser visibles para todo el equipo.

Resolución de problemas

Problema Causa Solución
ERROR_WRONG_USER_KEY Clave incorrecta o caducada Verifica la clave desde el panel de CaptchaAI
Caída de saldo inesperada Clave filtrada o compartida Rota la clave de inmediato y audita los accesos
La clave funciona en local pero no en CI Variable de entorno sin definir Añádela a los secrets de CI/CD
Clave en el historial de Git Se subió un archivo .env Rota la clave, añade .env a .gitignore y limpia el historial con git filter-branch

Checklist de seguridad

Práctica Estado
Clave API en variable de entorno
.env añadido a .gitignore
Sin claves en el código fuente
Claves redactadas en los logs
CI/CD usa el gestor de secrets
Calendario de rotación de claves
Monitoreo de saldo activo

Preguntas frecuentes

¿La lista blanca de IP sustituye a guardar la clave en variables de entorno?

No. Son capas distintas: las variables de entorno evitan que la clave se filtre, y la lista blanca de IP hace que una clave filtrada sea inútil fuera de tu red. Usa ambas — ninguna reemplaza a la otra.

¿Cómo sé si mi clave ya se ha filtrado?

Vigila el saldo: una caída que no corresponde a tu volumen habitual es la señal más clara. Revisa también los logs de uso en tu panel de CaptchaAI en busca de solicitudes desde IPs o a horas que no reconoces. Ante la duda, rota la clave: es una operación barata frente al coste de un saldo agotado.

¿Cada cuánto debería rotar la clave API?

Depende de tu exposición. Un buen punto de partida son 90 días de forma programada, más una rotación inmediata cada vez que alguien con acceso al panel deja el equipo o cuando sospechas de una filtración. La clase KeyManager de arriba te permite cambiar de clave sin downtime.

¿Necesito claves distintas para desarrollo y producción?

Sí. Usa claves separadas para desarrollo, staging y producción. Si se filtra la clave de desarrollo, el radio de impacto queda contenido y tu producción sigue intacta.

Guías relacionadas

Protege tu inversión: asegura tu clave API de CaptchaAI hoy mismo.

Los comentarios están deshabilitados para este artículo.