Tutoriels

CAPTCHA Résolution des journaux d'audit : suivi des demandes de résolution de conformité

Lorsque votre organisation résout quotidiennement des milliers de CAPTCHA, vous avez besoin d’un enregistrement de chaque demande. Les journaux d'audit répondent à des questions telles que : Qui a déclenché cette résolution ? C'était pour quel site ? Combien ça a coûté ? Quand est-ce arrivé? Ce guide montre comment mettre en œuvre une journalisation d'audit complète pourCaptchaAIopérations.

Quoi enregistrer

Chaque résolution CAPTCHA doit enregistrer :

Champ Objectif Exemple
timestamp Quand la demande a été faite 2026-04-04T14:30:00Z
request_id Identifiant unique pour cette résolution uuid4()
captcha_type Méthode CAPTCHA utilisée userrecaptcha
target_site URL de la page en cours de résolution https://example.com/login
task_id ID de tâche CaptchaAI 73829451
status Résultat solved, failed, timeout
solve_time_ms Temps entre la soumission et le résultat 18432
error_code Erreur en cas d'échec ERROR_CAPTCHA_UNSOLVABLE
initiator Qui ou quoi a déclenché la résolution scraper-job-42
cost Coût estimé 0.003

Ne consignez pas : les clés API, les jetons CAPTCHA (ils sont temporaires) ou les informations personnelles identifiables provenant des sites cibles.

Implémentation Python

# audit_solver.py
import os
import uuid
import time
import json
import logging
from datetime import datetime, timezone
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

# Configure audit logger — separate from application logs
audit_logger = logging.getLogger("captcha_audit")
audit_logger.setLevel(logging.INFO)

# File handler with rotation
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
    "captcha_audit.jsonl",
    maxBytes=50_000_000,  # 50 MB per file
    backupCount=10,
)
handler.setFormatter(logging.Formatter("%(message)s"))
audit_logger.addHandler(handler)

def log_audit(record):
    """Write a structured audit record."""
    audit_logger.info(json.dumps(record, default=str))

def solve_with_audit(sitekey, pageurl, captcha_type="userrecaptcha",
                      initiator="unknown"):
    """Solve a CAPTCHA with full audit logging."""
    request_id = str(uuid.uuid4())
    start = time.time()

    audit_record = {
        "request_id": request_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "captcha_type": captcha_type,
        "target_site": pageurl,
        "initiator": initiator,
        "status": "submitted",
    }

    session = requests.Session()

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

        if result.get("status") != 1:
            audit_record.update({
                "status": "submit_failed",
                "error_code": result.get("request"),
                "solve_time_ms": int((time.time() - start) * 1000),
            })
            log_audit(audit_record)
            return None

        task_id = result["request"]
        audit_record["task_id"] = task_id

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

            if poll_result.get("status") == 1:
                solve_time = int((time.time() - start) * 1000)
                audit_record.update({
                    "status": "solved",
                    "solve_time_ms": solve_time,
                    "cost_estimate": 0.003,  # Adjust per your rate
                })
                log_audit(audit_record)
                return poll_result["request"]

            if poll_result.get("request") != "CAPCHA_NOT_READY":
                audit_record.update({
                    "status": "failed",
                    "error_code": poll_result.get("request"),
                    "solve_time_ms": int((time.time() - start) * 1000),
                })
                log_audit(audit_record)
                return None

            time.sleep(5)

        audit_record.update({
            "status": "timeout",
            "solve_time_ms": int((time.time() - start) * 1000),
        })
        log_audit(audit_record)
        return None

    except Exception as e:
        audit_record.update({
            "status": "error",
            "error_code": str(e)[:200],
            "solve_time_ms": int((time.time() - start) * 1000),
        })
        log_audit(audit_record)
        raise

# Usage
token = solve_with_audit(
    sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    pageurl="https://www.google.com/recaptcha/api2/demo",
    initiator="price-scraper-v2",
)

Sortie du journal d'audit (format JSONL)

{"request_id":"a1b2c3d4-...","timestamp":"2026-04-04T14:30:00+00:00","captcha_type":"userrecaptcha","target_site":"https://www.google.com/recaptcha/api2/demo","initiator":"price-scraper-v2","status":"solved","task_id":"73829451","solve_time_ms":18432,"cost_estimate":0.003}

Implémentation JavaScript

// audit_solver.js
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const AUDIT_FILE = 'captcha_audit.jsonl';

function logAudit(record) {
  fs.appendFileSync(AUDIT_FILE, JSON.stringify(record) + '\n');
}

async function solveWithAudit(sitekey, pageurl, initiator = 'unknown') {
  const requestId = uuidv4();
  const start = Date.now();
  const record = {
    request_id: requestId,
    timestamp: new Date().toISOString(),
    captcha_type: 'userrecaptcha',
    target_site: pageurl,
    initiator,
    status: 'submitted',
  };

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

    if (submit.data.status !== 1) {
      record.status = 'submit_failed';
      record.error_code = submit.data.request;
      record.solve_time_ms = Date.now() - start;
      logAudit(record);
      return null;
    }

    record.task_id = 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: API_KEY, action: 'get', id: submit.data.request, json: '1' },
      });

      if (poll.data.status === 1) {
        record.status = 'solved';
        record.solve_time_ms = Date.now() - start;
        record.cost_estimate = 0.003;
        logAudit(record);
        return poll.data.request;
      }
      if (poll.data.request !== 'CAPCHA_NOT_READY') {
        record.status = 'failed';
        record.error_code = poll.data.request;
        record.solve_time_ms = Date.now() - start;
        logAudit(record);
        return null;
      }
      await new Promise(r => setTimeout(r, 5000));
    }

    record.status = 'timeout';
    record.solve_time_ms = Date.now() - start;
    logAudit(record);
    return null;
  } catch (e) {
    record.status = 'error';
    record.error_code = e.message.slice(0, 200);
    record.solve_time_ms = Date.now() - start;
    logAudit(record);
    throw e;
  }
}

Interrogation des journaux d'audit

Résumé quotidien

import json
from collections import Counter
from datetime import date

def daily_summary(log_file, target_date=None):
    """Generate a daily summary from audit logs."""
    target = target_date or date.today().isoformat()
    statuses = Counter()
    total_cost = 0
    solve_times = []

    with open(log_file) as f:
        for line in f:
            record = json.loads(line)
            if record["timestamp"].startswith(target):
                statuses[record["status"]] += 1
                total_cost += record.get("cost_estimate", 0)
                if record.get("solve_time_ms"):
                    solve_times.append(record["solve_time_ms"])

    print(f"Date: {target}")
    print(f"Total requests: {sum(statuses.values())}")
    print(f"Statuses: {dict(statuses)}")
    print(f"Estimated cost: ${total_cost:.2f}")
    if solve_times:
        print(f"Median solve time: {sorted(solve_times)[len(solve_times)//2]}ms")

daily_summary("captcha_audit.jsonl")

Rétention et stockage

Volume Taille du journal quotidien Stockage mensuel Recommandation
100 solutions/day ~30 Ko ~1 Mo Fichier local
1 000 solutions/day ~300 Ko ~10 Mo Fichier local + rotation
10 000 solutions/day ~3 Mo ~100 Mo Expédier à l'agrégateur de journaux
100 000 solutions/day ~30 Mo ~1 Go Journalisation centralisée (ELK, Datadog)

Dépannage

Problème Parce que Corriger
Le fichier journal devient trop volumineux Aucune rotation configurée Utilisez RotatingFileHandler ou logrotate
Dossiers d'audit manquants Exception avant la journalisation Connectez-vous au bloc finally
Écritures lentes à volume élevé Fichier synchrone I/O Utiliser des écritures de fichiers asynchrones ou un tampon
Horodatages incohérents Dérive de l'horloge système Utilisez NTP ; se connecter en UTC

FAQ

Dois-je enregistrer le jeton CAPTCHA dans la piste d'audit ?

Non. Les jetons sont temporaires (expirent dans 60 à 300 secondes) et n'ont aucune valeur d'audit. Leur journalisation augmente le stockage sans avantage.

Puis-je utiliser les journaux d’audit pour le rapprochement des factures ?

Oui. Comparez les totaux de vos journaux d'audit avec le tableau de bord d'utilisation de CaptchaAI pour vérifier l'exactitude de la facturation.

Quelle durée de conservation dois-je définir ?

90 jours est la norme pour les journaux d’audit opérationnels. Pour une journalisation axée sur la conformité, vérifiez les exigences de votre secteur (SOC 2, GDPR, HIPAA).

Articles connexes

  • Suivi de résolution de Captcha sans serveur Dynamodb

Prochaines étapes

Ajoutez de la responsabilité à chaque résolution de CAPTCHA –récupérez votre clé API CaptchaAI.

Guides associés :

  • Surveillance du tableau de bord d'utilisation
  • Journalisation structurée
  • Vérification du solde et recharge automatique
Les commentaires sont désactivés pour cet article.