Integrations

Integrasi HashiCorp Vault untuk Manajemen API Key CaptchaAI

Simpan API key CaptchaAI di HashiCorp Vault, lalu ambil saat runtime — bukan menaruhnya di .env yang ikut ter-commit atau di variabel environment yang terbaca di log crash. Dengan pola ini kunci hidup terenkripsi di satu tempat, setiap pembacaan tercatat lengkap dengan identitas pemanggilnya, dan Anda bisa mengganti kunci tanpa menyentuh satu baris kode pun.

Tim kecil biasanya mulai dengan satu API key bersama: masuk ke repo, dikirim lewat Slack, menempel di image Docker. Saat satu developer keluar, tidak ada yang tahu kunci mana yang bocor. Contoh di bawah memakai reCAPTCHA v2 lewat in.php dan res.php, tetapi polanya sama untuk Turnstile maupun GeeTest v3.

Apa yang berubah setelah API key pindah ke Vault

Sebelum Vault Sesudah Vault
API key ada di file .env atau di dalam kode Kunci tersimpan terenkripsi di Vault
Kunci dibagikan lewat Slack atau email Diambil lewat API yang terautentikasi
Tidak ada jejak audit siapa membaca apa Setiap pembacaan tercatat dengan identitas
Rotasi kunci dikerjakan manual Rotasi otomatis didukung
Satu kunci dipakai di semua environment Satu kunci per environment dengan policy sendiri

Vault tidak mengubah tagihan Anda

Perlu ditegaskan sejak awal: Vault tidak mengubah cara CaptchaAI menagih. Paket CaptchaAI berbasis thread — BASIC ($15/bulan) sampai VIP-3 ($7,500/bulan). Memecah kunci per environment tidak menambah biaya; yang Anda bagi hanya kapasitas thread di paket yang sama.

Yang perlu disiapkan

Kebutuhan Catatan
Server HashiCorp Vault Self-hosted atau HCP Vault
Akses Vault CLI atau API Untuk menulis secret dan policy
API key CaptchaAI Kunci yang akan dipindahkan ke Vault
Python 3.8+ atau Node.js 18+ Runtime worker pada contoh di bawah

Langkah 1: simpan API key di KV engine

# 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

Langkah 2: buat policy read-only untuk worker

Worker penyelesai CAPTCHA hanya perlu membaca. Jangan beri kemampuan menulis pada policy yang dipakai proses produksi:

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

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

Terapkan policy tersebut:

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

Langkah 3: ambil kunci dari Vault di Python

Kode berikut mengambil kunci sekali saat objek dibuat, lalu menyegarkannya setiap jam. Pola cache-plus-refresh ini penting: memanggil Vault pada setiap solve menambah satu round trip jaringan ke jalur yang seharusnya cepat.

# 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]}...")

Langkah 4: pola yang sama di Node.js

Versi JavaScript memakai HTTP API Vault langsung lewat Axios, tanpa SDK tambahan. Perhatikan init() yang terpisah dari constructor — pengambilan kunci bersifat async, jadi tidak bisa dikerjakan di dalam constructor.

// 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)}...`);
})();

Memilih metode autentikasi Vault

Metode Cocok untuk Yang disiapkan
Token Development, CI/CD Environment variable VAULT_TOKEN
AppRole Layanan produksi Role ID + Secret ID
Kubernetes Beban kerja di K8s JWT service account
AWS IAM Worker di EC2 atau Lambda IAM role pada instance

AppRole: pilihan default untuk produksi

Token statis punya masalah klasik — ia harus disimpan di suatu tempat, dan tempat itu jadi rahasia baru yang perlu dijaga. AppRole memutus rantai tersebut: worker login dengan Role ID dan Secret ID, lalu menerima token berumur pendek yang bisa diperpanjang.

# 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"]

Konteks deployment di Indonesia

Tempatkan Vault di region yang sama dengan worker

Tim otomasi di Indonesia umumnya menjalankan worker di AWS ap-southeast-1 (Singapura), ap-southeast-3 (Jakarta), atau GCP asia-southeast2 (Jakarta). Taruh server Vault di region yang sama: panggilan lintas region menambah puluhan milidetik pada tiap refresh, dan bila jalur antar-region terganggu, worker ikut berhenti meski CaptchaAI sendiri sehat.

Satu developer, banyak klien

Skenario yang sering muncul di kerja kontrak (Upwork, Fastwork, dan sejenisnya): satu developer memegang beberapa klien sekaligus. Alih-alih menyimpan API key tiap klien di file terpisah pada laptop:

  • buat satu path Vault per klien — secret/captchaai/klien-a, secret/captchaai/klien-b
  • pasang policy read-only terpisah untuk tiap path
  • cabut policy-nya saat kontrak berakhir; tidak ada file sisa yang perlu diburu di disk

Catatan kepatuhan

UU Pelindungan Data Pribadi (UU 27/2022) menuntut kontrol akses yang bisa dibuktikan atas kredensial sistem yang menyentuh data pengguna. Audit log Vault memberi bukti itu otomatis — bukan nasihat hukum, tetapi jauh lebih mudah dijelaskan saat audit dibanding sebaris kunci di .env.

Rotasi kunci tanpa deploy ulang

  1. Buat API key CaptchaAI baru di dashboard CaptchaAI
  2. Perbarui Vault: vault kv put secret/captchaai api_key="NEW_KEY"
  3. Worker mengambil kunci baru sendiri pada siklus refresh berikutnya
  4. Cabut kunci lama di dashboard setelah semua worker selesai menyegarkan

Tidak ada perubahan kode, tidak ada deployment. Jarak langkah 2 ke 4 minimal sepanjang _key_refresh_interval — dengan 3600 detik pada contoh di atas, tunggu satu jam sebelum mencabut kunci lama.

Ketika terjadi masalah

Sebagian besar kegagalan integrasi Vault berhenti pada gejala-gejala berikut.

Gejala Sebab Perbaikan
Vault membalas 403 Forbidden Policy tidak mengizinkan pembacaan Cek path di captcha-worker-policy.hcl
VAULT_TOKEN kedaluwarsa TTL token terlampaui Pindah ke AppRole agar token diperpanjang otomatis
Kunci lama masih dipakai Interval refresh terlalu panjang Perkecil _key_refresh_interval
Vault tidak bisa dihubungi Gangguan jaringan atau server Pakai kunci yang sudah di-cache sebagai fallback
CAPCHA_NOT_READY terus muncul Bukan kesalahan Vault Task memang belum selesai — lanjutkan polling

Catatan: CAPCHA_NOT_READY datang dari res.php, bukan dari Vault, dan artinya normal. Teruskan polling sesuai jeda pada contoh kode.

Pertanyaan Umum

Apakah worker berhenti kalau Vault sedang down?

Tidak, selama kunci di-cache di memori seperti pada contoh kode. Kegagalan refresh cukup dicatat di log; worker tetap memakai kunci terakhir yang valid. Yang berisiko adalah memanggil Vault pada setiap solve tanpa cache — di situ Vault menjadi single point of failure.

Berapa sering sebaiknya kunci diambil ulang dari Vault?

Satu jam wajar untuk worker jangka panjang. Perpendek ke 5–15 menit hanya bila Anda memang merotasi kunci sesering itu; refresh terlalu agresif menambah beban Vault tanpa manfaat keamanan nyata.

Apakah pola ini berlaku untuk Turnstile dan GeeTest v3?

Ya. Yang berubah hanya parameter method saat mengirim task ke in.php; pengambilan kunci dari Vault persis sama. Perlu diingat hCaptcha, FunCaptcha, dan GeeTest v4 belum didukung CaptchaAI, sedangkan CaptchaFox (beta), Friendly Captcha (beta), dan Lemin (beta) masih berstatus beta.

Bisakah AWS Secrets Manager dipakai menggantikan Vault?

Bisa, polanya identik — ambil secret saat runtime lewat boto3, cache di memori, refresh berkala. Vault lebih unggul bila Anda berjalan di beberapa cloud; Secrets Manager lebih praktis bila seluruh worker sudah di AWS.

Perlukah kunci yang berbeda untuk tiap environment?

Ya. Pakai path terpisah: secret/captchaai/dev, secret/captchaai/staging, secret/captchaai/prod. Karena tagihan CaptchaAI berbasis thread, memisahkan kunci per environment tidak menaikkan biaya — Anda hanya membagi kapasitas thread yang sudah ada.

Artikel Terkait

Langkah Selanjutnya

Pindahkan API key CaptchaAI Anda dari file .env ke Vault hari ini — ambil API key Anda, simpan di KV engine, lalu jalankan worker pertama dengan policy read-only.

Panduan terkait:

Komentar dinonaktifkan untuk artikel ini.