Troubleshooting

Expiry Token reCAPTCHA: Timing Window dan Race Condition

Kesalahan timeout-or-duplicate adalah kegagalan reCAPTCHA yang paling umum dalam alur kerja otomatisasi. Artinya token Anda sudah habis masa berlakunya sebelum dikirimkan atau digunakan dua kali. Karena token reCAPTCHA hanya berdurasi 120 detik dan pemecah API memerlukan waktu 15-45 detik untuk membuatnya, manajemen waktu sangatlah penting. Panduan ini mencakup mekanisme expiry yang tepat, race condition umum, dan strategi pengaturan waktu praktis.


Aturan 120 detik

Setiap token reCAPTCHA akan expiry tepat 120 detik setelah pembuatan. Ini berlaku untuk semua versi:

Token generated (solve complete)
    ├─── 0s:   Token is valid ✓
    ├─── 60s:  Token is valid ✓
    ├─── 110s: Token is valid ✓  (but cutting it close)
    ├─── 119s: Token is valid ✓  (dangerous territory)
    └─── 120s: Token EXPIRED ✗  (timeout-or-duplicate)

Dimana pengatur waktu dimulai

Skenario Pengatur waktu dimulai ketika...
kotak centang reCAPTCHA v2 Pengguna mencentang kotak (tantangan terpecahkan)
Kisi gambar reCAPTCHA v2 Pengguna menyelesaikan putaran pemilihan gambar akhir
reCAPTCHA v2 invisible Callback execute() diaktifkan dengan token
reCAPTCHA v3 execute() Janji diselesaikan dengan token
Pemecah API (CaptchaAI) Solver menghasilkan token (BUKAN saat Anda menerimanya)

Kesenjangan waktu pemecah

Saat menggunakan pemecah API, ada penundaan antara saat pemecah membuat token dan saat kode Anda menerimanya:

Solver generates token (timer starts)
    ↓ ~1-5 seconds (network + polling interval)
Your code receives token via res.php poll
    ↓ You now have ~115-119 seconds remaining
Your code processes and submits token
    ↓ ~1-10 seconds (depends on your workflow)
Target website validates token with Google
    ↓ ~1-2 seconds (Google API response time)
Total remaining after validation: ~105-117 seconds (comfortable)

Dalam praktiknya, jendela 120 detik memberikan waktu yang cukup ketika kode Anda dikirimkan segera setelah menerima token. Masalah terjadi ketika:

  • Kode Anda memiliki langkah pemrosesan tambahan antara menerima token dan mengirimkannya
  • Beberapa formulir perlu diisi sebelum diserahkan
  • Situs web target memiliki waktu respons yang lambat
  • Anda mengantri token dan mengirimkannya nanti

Race condition umum

Race condition 1: Solve paralel + penyerahan berurutan

# WRONG: Solving multiple CAPTCHAs in parallel, then submitting sequentially
tokens = []
for url in urls:
    task_id = solve_captcha(url)  # All submitted at t=0
    tokens.append(task_id)

# All tokens arrive around t=30
solved_tokens = [poll_result(tid) for tid in tokens]

# Sequential submission: first token at t=32, last at t=120+
for i, (url, token) in enumerate(zip(urls, solved_tokens)):
    submit_form(url, token)  # Later tokens may be expired!
    time.sleep(10)  # Each wait adds pressure

Perbaikan: Selesaikan dan kirimkan setiap CAPTCHA sebelum memulai yang berikutnya:

# CORRECT: Solve and submit one at a time
for url in urls:
    token = solve_and_wait(url)  # Token received at t=30
    submit_form(url, token)      # Submitted at t=31 (89 seconds remaining)

Race condition 2: Pengambilan awal token

# WRONG: Pre-fetching tokens before knowing when they'll be used
token = solve_captcha()  # Token received at t=0

# ... user fills out form (30-120+ seconds) ...
# ... validation checks ...
# ... other processing ...

submit_form(token)  # Token may be expired!

Perbaikan: Selesaikan CAPTCHA sebagai langkah terakhir sebelum pengiriman:

# CORRECT: Late-bind the CAPTCHA solve
prepare_form_data()   # Do everything that doesn't need the token
validate_inputs()     # Run validation before spending a token

# Now solve and submit immediately
token = solve_captcha()  # Token received at t=0
submit_form(token)       # Submitted at t=1 (119 seconds remaining)

Race condition 3: Pengiriman formulir multi-langkah

# PROBLEMATIC: Multi-step form where CAPTCHA is on step 1 but submit is step 3
token = solve_captcha()       # t=0: Token received

fill_step_1(token)            # t=5: Step 1 submitted
response = fill_step_2()      # t=15: Step 2 completed
# ... step 2 has additional verification ...
wait_for_verification()       # t=60: Verification complete
fill_step_3_and_submit()      # t=65: Final submission (55 seconds remaining - OK)
# BUT if step 2 takes longer than expected...

Perbaikan: Ukur usia token dan selesaikan kembali jika perlu:

token_received_at = time.time()
token = solve_captcha()
token_received_at = time.time()

# ... multi-step process ...

# Before final submission, check token age
token_age = time.time() - token_received_at
if token_age > 100:  # 20-second safety margin
    print(f"Token is {token_age:.0f}s old — requesting fresh token")
    token = solve_captcha()
    token_received_at = time.time()

submit_final(token)

Manajer waktu token

Kelas siap produksi untuk mengelola masa pakai token:

import time
import requests

class TokenTimingManager:
    """Manage reCAPTCHA token timing to prevent expiration errors."""

    API_KEY = "YOUR_API_KEY"
    TOKEN_LIFETIME = 120
    SAFETY_MARGIN = 15  # seconds before expiry to consider "stale"

    def __init__(self, site_key, page_url, version="v2"):
        self.site_key = site_key
        self.page_url = page_url
        self.version = version
        self.current_token = None
        self.token_timestamp = None

    def _solve(self):
        """Request and poll for a new token."""
        params = {
            "key": self.API_KEY,
            "method": "userrecaptcha",
            "googlekey": self.site_key,
            "pageurl": self.page_url,
            "json": 1,
        }
        if self.version == "v3":
            params.update({"version": "v3", "action": "submit"})

        submit = requests.post("https://ocr.captchaai.com/in.php", data=params).json()
        task_id = submit["request"]

        for _ in range(60):
            time.sleep(5)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.API_KEY,
                "action": "get",
                "id": task_id,
                "json": 1,
            }).json()

            if result.get("status") == 1:
                self.current_token = result["request"]
                self.token_timestamp = time.time()
                return self.current_token

        raise TimeoutError("Token solve timeout")

    @property
    def token_age(self):
        """Seconds since current token was received."""
        if self.token_timestamp is None:
            return float("inf")
        return time.time() - self.token_timestamp

    @property
    def token_remaining(self):
        """Seconds remaining before token expires."""
        return max(0, self.TOKEN_LIFETIME - self.token_age)

    @property
    def is_fresh(self):
        """Whether the token is fresh enough to use."""
        return self.token_remaining > self.SAFETY_MARGIN

    def get_token(self):
        """Get a valid token, solving if current is stale or missing."""
        if self.current_token and self.is_fresh:
            return self.current_token

        return self._solve()

    def use_token(self):
        """Get and consume a token (cannot be reused)."""
        token = self.get_token()
        # Mark as consumed
        self.current_token = None
        self.token_timestamp = None
        return token

# Usage
manager = TokenTimingManager(
    site_key="6LcR_RsTAAAAAN_r0GEkGBfq3L7KmU5JbPHJtwNp",
    page_url="https://staging.example.com/qa-login",
)

# Get a fresh token right before submission
token = manager.use_token()
print(f"Token remaining: {manager.TOKEN_LIFETIME}s (fresh solve)")

# Submit form with token...

Menangani kesalahan timeout atau duplikat

Saat Anda menerima timeout-or-duplicate, diagnosis penyebabnya:

def handle_recaptcha_error(error_codes, token_age_seconds):
    """Diagnose and handle reCAPTCHA validation errors."""

    if "timeout-or-duplicate" in error_codes:
        if token_age_seconds > 120:
            return {
                "cause": "Token expired (age: {:.0f}s > 120s)".format(token_age_seconds),
                "fix": "Reduce time between receiving and submitting token",
                "action": "re-solve",
            }
        elif token_age_seconds < 5:
            return {
                "cause": "Token likely reused (duplicate submission)",
                "fix": "Ensure each form submission gets a unique token",
                "action": "re-solve",
            }
        else:
            return {
                "cause": "Token may have been reused or server-side timing issue",
                "fix": "Check for double-submit in form handler",
                "action": "re-solve",
            }

    if "invalid-input-response" in error_codes:
        return {
            "cause": "Token is malformed or corrupted",
            "fix": "Check token transmission (URL encoding, field name)",
            "action": "re-solve",
        }

    return {"cause": "Unknown", "action": "investigate"}

Praktik terbaik pengaturan waktu

Praktek Rekomendasi
Solusi-untuk-menyerahkan kesenjangan Tetap di bawah 90 detik (margin 30 detik)
Interval polling 5 detik antara pemeriksaan res.php
validasi awal Hanya pra-solve jika Anda tahu pengiriman akan dilakukan dalam waktu 60 detik
Coba lagi setelah habis masa berlakunya Selalu minta token baru di timeout-or-duplicate
Antrian token Jangan pernah mengantri token — masing-masing token akan expiry secara independen
Operasi paralel Selesaikan per tugas, bukan selesaikan semuanya lalu gunakan
Pemantauan Lacak usia token pada waktu pengiriman

Pertanyaan yang sering diajukan

Bisakah saya memperpanjang jendela token 120 detik?

Tidak. Masa berlaku ini diberlakukan di sisi server oleh Google dan tidak dapat diperpanjang dengan cara apa pun. 120 detik adalah batas yang sulit.

Apakah penundaan polling mengurangi waktu penggunaan token saya?

Ya, sedikit. Jika Anda melakukan polling setiap 5 detik, Anda dapat menerima token hingga 5 detik setelah dibuat. Dalam praktiknya, ini berarti Anda memiliki waktu ~115 detik, bukan 120. Ini biasanya tidak menjadi masalah.

Haruskah saya menyelesaikan token terlebih dahulu agar siap?

Hanya jika Anda dapat menjamin token akan dikirimkan dalam waktu 60-90 detik. validasi awal berguna untuk alur kerja yang sensitif terhadap latensi (misalnya, pembelian kompetitif) yang memerlukan tambahan waktu solve 15-45 detik. Untuk sebagian besar alur kerja, solve sesuai permintaan lebih aman.

Mengapa Google menggunakan kesalahan yang sama untuk token yang expiry dan duplikat?

Google sengaja mengaburkan perbedaan tersebut untuk mencegah skrip otomatisasi menggunakan kode kesalahan untuk mendeteksi kasus mana yang terjadi. Kedua kasus tersebut memiliki solusi yang sama: meminta token baru.


Ringkasan

Token reCAPTCHA akan habis masa berlakunya tepat setelah 120 detik dan hanya dapat digunakan satu kali. Kesalahan otomatisasi yang paling umum — timeout-or-duplicate — disebabkan oleh keterlambatan pengiriman token atau penggunaan kembali token. Saat menggunakanCaptchaAI, selesaikan token sebelum pengiriman, lacak usia token, dan jangan pernah mengantri atau menggunakan kembali token. Gunakan pola manajer waktu di atas untuk secara otomatis menangani expiry dan solve dalam alur kerja produksi.

Artikel Terkait

Komentar dinonaktifkan untuk artikel ini.