API Tutorials

CaptchaAI IP白名单和API密钥安全

很多团队第一次意识到 API Key 泄露,不是从代码审计发现的,而是从余额突然被刷空开始的。CaptchaAI 的 API Key 直接绑定账户余额,谁拿到字符串谁就能调用识别接口消耗额度。本文按存储、轮换、日志、CI/CD 的顺序讲清楚怎么防,最后说说 IP 白名单能做哪些限制。


密钥为什么会泄露:常见场景

泄露路径其实来来回回就那几种:

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

不管哪一种,结果都一样:余额和配额被别人用掉。先照下面的清单自查,再往下逐条展开。


安全清单自查

实践 状态
API 密钥放在环境变量里
.env 已加入 .gitignore
源代码中没有明文密钥
日志里的密钥已脱敏
CI/CD 使用 secrets 管理器
有密钥轮换计划
余额监控已启用

Key 存储:从环境变量开始

别把 Key 写死在代码里

硬编码是最常见也最容易被忽视的错误——代码一旦提交,字符串就永久留在 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"]

用 .env 管理本地密钥

本地开发用 .env 存密钥,别让它被提交:

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

别让 .env 混进 Git 历史

在 .gitignore 里排除所有 .env 变体,避免误传:

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

按环境拆分配置

把配置封装成一个类,启动时就校验密钥,而不是等线上报错才发现:

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}")

密钥轮换

密钥不该一直用到过期或泄露才换。给账号备一把备用密钥,主密钥出问题时立即切换:

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()

真实场景:仓库一旦公开,GitHub 的 secret scanning 或 gitleaks 几分钟内就能扫到 CAPTCHAAI_API_KEY 这种字符串,恶意脚本只会更快。加个 pre-commit 钩子提前扫,比等余额清零才发现划算。


请求参数校验

调用前先校验参数,能省不少排查时间——尤其是 pageurlmethod 这两个最容易传错的字段:

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}")

日志脱敏:别把密钥打进日志

日志也是泄露高发地——尤其是把整个请求体原样打印调试的时候。给 Formatter 加一层正则过滤,自动替换掉疑似密钥的字符串:

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]

容器化部署中的密钥管理

用 Docker 部署时同样不要把密钥写进镜像——用环境变量或 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

CI/CD 流水线安全

GitHub Actions

密钥应该存在平台自带的 secrets 管理器里,而不是写进 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

切勿在 CI 输出中打印或回显密钥——哪怕只是调试信息,也可能被日志系统完整保留,而多数 CI 平台的日志默认团队内可见。


常见问题

API Key 不小心提交到公开仓库了,该怎么办?

删文件不会让密钥从 Git 历史消失。立即登录 CaptchaAI 控制台生成新密钥、让旧密钥失效,再查余额是否异常。

CaptchaAI 控制台可以设置 IP 白名单吗?

在控制台的 IP 限制设置里查看当前支持的选项,开放后只把服务器固定出口 IP 加入白名单,其余请求会被拒绝。

.env 文件已经够安全了吗,需要额外加密吗?

.env 只解决了"密钥不进 Git 仓库"这一层,本身不加密。本地开发够用,生产环境更稳妥的做法是用平台自带的 secrets 管理器。

余额突然被消耗很快,怎么第一时间确认是不是密钥泄露?

看请求来源和频率——陌生 IP 的密集请求基本能确定是泄露。先轮换密钥止损,再查日志找泄露点。

IP 白名单和密钥轮换可以一起用吗?

可以,两者不冲突。白名单限制请求来源,轮换降低单个密钥长期暴露的风险,同时启用能进一步收窄攻击面。


常见故障排查

问题 原因 处理方式
ERROR_WRONG_USER_KEY 密钥错误或已过期 登录 CaptchaAI 控制台核对密钥
余额突然被消耗 密钥泄露或被共享 立即轮换密钥,排查访问记录
本地能用,CI 里报错 环境变量没配置 添加到 CI/CD 的 secrets 管理器
Git 历史里留有旧密钥 曾经提交过 .env 文件 轮换密钥,把 .env 加入 .gitignore,再用 git filter-branch 清理历史

相关指南


把 CaptchaAI API Key 用得更安全,从今天开始。

该文章已禁用评论。