Explainers

Aturan Cloudflare WAF yang Memicu CAPTCHA Challenge

CAPTCHA Cloudflare tidak muncul secara acak. Di baliknya ada aturan Web Application Firewall (WAF) yang dibuat operator situs untuk memunculkan challenge saat sebuah request cocok dengan kondisi tertentu — IP, negara, path URL, bot score, atau header. Dari sekian banyak tindakan WAF, hanya tiga yang menghasilkan challenge yang bisa Anda selesaikan; sisanya sekadar meloloskan atau memblokir request. Mengenali tindakan mana yang aktif adalah kunci memilih penanganan yang tepat.


Tindakan WAF yang menghasilkan challenge yang bisa di-solve

Aturan WAF mendukung beberapa tindakan. Hanya tiga baris pertama di bawah yang menghadirkan challenge yang dapat di-solve:

Tindakan WAF Apa yang terjadi Status HTTP Metode CaptchaAI
Managed Challenge Cloudflare memutuskan: invisible challenge, Turnstile, atau JS 503 turnstile
JS Challenge Halaman JavaScript challenge 5 detik 503 cloudflare_challenge
Interactive Challenge CAPTCHA tradisional (legacy, deprecated) 403 turnstile
Block Hard 403, tidak ada challenge 403 N/A (tidak bisa di-solve)
Allow Lewati, tanpa pemeriksaan 200 N/A
Skip Lewati aturan WAF lainnya 200 N/A
Log Catat event, tidak ada tindakan 200 N/A

Aturan bacanya: status 503 menandai challenge aktif yang bisa diselesaikan, 403 dari Block adalah penolakan penuh.

Managed Challenge: pilihan adaptif yang paling sering dipakai

Managed Challenge direkomendasikan Cloudflare dan paling sering muncul. Cloudflare menilai tiap pengunjung lalu memilih respons yang sesuai:

WAF rule matches → Managed Challenge triggered
    ↓
Cloudflare evaluates visitor:
  ├─ Low risk → Invisible pass (no visible challenge)
  ├─ Medium risk → Turnstile widget (click to verify)
  └─ High risk → JavaScript challenge page
    ↓
Successful → qa_validation_cookie cookie issued

Karena itu request otomatis berisiko tinggi lebih sering mendapat halaman JS challenge dibanding browser biasa.


Pola aturan WAF yang paling sering memicu challenge

Aturan WAF ditulis dengan bahasa ekspresi Cloudflare. Berikut pola pemicu challenge yang paling umum untuk traffic otomatis.

Aturan bot score adalah pemicu nomor satu. Solver CaptchaAI mendapat skor setingkat manusia karena berjalan di browser nyata.

# Challenge traffic with low bot scores
(cf.bot_management.score lt 30)
→ Action: Managed Challenge

# Challenge non-verified bots
(cf.bot_management.score lt 50 and not cf.bot_management.verified_bot)
→ Action: JS Challenge

Aturan berdasarkan negara menantang traffic dari negara tertentu, atau memblokirnya total.

# Challenge traffic from specific countries
(ip.geoip.country in {"CN" "RU" "VN" "IN"})
→ Action: Managed Challenge

# Block specific regions entirely
(ip.geoip.country eq "XX")
→ Action: Block

Aturan berdasarkan path melindungi halaman sensitif seperti /login atau endpoint /api/.

# Challenge login page access
(http.request.uri.path eq "/login" or http.request.uri.path eq "/signup")
→ Action: Managed Challenge

# Challenge API endpoints
(http.request.uri.path contains "/api/")
→ Action: JS Challenge

Aturan berdasarkan rate request memicu challenge saat request ke satu path melonjak.

# Challenge after high request rate
(cf.threat_score gt 10 and http.request.uri.path contains "/search")
→ Action: Managed Challenge

Aturan berdasarkan header menandai request tanpa Accept-Language atau dengan User-Agent mencurigakan.

# Challenge requests with no Accept-Language header
(not http.request.headers["accept-language"])
→ Action: JS Challenge

# Challenge requests with suspicious UA
(http.user_agent contains "python" or http.user_agent contains "curl")
→ Action: Managed Challenge

Aturan gabungan menyatukan beberapa kondisi sekaligus — dan paling membingungkan saat request tiba-tiba kena challenge.

# Multiple conditions
(cf.bot_management.score lt 30
 and http.request.uri.path contains "/api/"
 and ip.geoip.country ne "US")
→ Action: JS Challenge

Cara mengenali aturan mana yang memicu challenge

Aturan WAF tak bisa dibaca langsung, tapi bisa disimpulkan dari respons.

Membaca header dan body respons

import requests

def check_cloudflare_rule_info(url):
    """Extract WAF rule information from Cloudflare challenge response."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
        "Accept": "text/html,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
    }

    response = requests.get(url, headers=headers, timeout=15, allow_redirects=False)

    info = {
        "status": response.status_code,
        "cf_ray": response.headers.get("cf-ray", ""),
        "cf_cache_status": response.headers.get("cf-cache-status", ""),
        "server": response.headers.get("server", ""),
    }

    # Challenge-specific info
    html = response.text

    if response.status_code == 503:
        if "jschl" in html:
            info["challenge_type"] = "JS Challenge (IUAM or WAF rule)"
        elif "challenge-platform" in html:
            info["challenge_type"] = "Managed Challenge"
        elif "cf-turnstile" in html:
            info["challenge_type"] = "Turnstile (Managed Challenge)"

    elif response.status_code == 403:
        if "cf-ray" in str(response.headers):
            info["challenge_type"] = "WAF Block (no challenge)"
        else:
            info["challenge_type"] = "Origin 403 (not Cloudflare)"

    return info

Melacak lewat Cloudflare Ray ID

Setiap respons menyertakan header cf-ray. Operator situs bisa melacak Ray ID ini di dashboard (Security > Events) untuk melihat aturan yang dipicu.


Konteks lokal: worker scraping yang deploy dari Jakarta

Freelancer scraping dan agensi pemantau harga di Indonesia kerap menjalankan worker di region terdekat seperti AWS ap-southeast-3 (Jakarta), tapi IP pusat data justru sering memicu aturan bot score dan memunculkan 503. Model harga berbasis thread cocok untuk pasar sensitif biaya: paket BASIC ($15/bulan, 5 thread) menagih per thread, bukan per solve, jadi lonjakan challenge tidak menaikkan biaya. Sesuai UU Pelindungan Data Pribadi (UU 27/2022), scrape hanya data yang boleh Anda proses; contoh di sini pakai staging.example.com.


Cara solve challenge yang dipicu WAF

Apa pun jenisnya, alurnya sama:

  1. Kirim task ke in.php dengan metode yang sesuai.
  2. Simpan task ID dari respons.
  3. Polling res.php sampai status siap.
  4. Pakai token yang dikembalikan.
import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_cloudflare_challenge(url, challenge_type):
    """Solve Cloudflare challenge based on WAF rule action."""

    if challenge_type == "managed_challenge":
        # Managed Challenge typically renders as Turnstile
        method = "turnstile"
        sitekey = extract_turnstile_sitekey(url)
    elif challenge_type == "js_challenge":
        # JavaScript Challenge page
        method = "cloudflare_challenge"
        sitekey = "managed"
    else:
        raise ValueError(f"Unknown challenge type: {challenge_type}")

    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": method,
        "sitekey": sitekey,
        "pageurl": url,
        "json": 1,
    })

    task_id = submit.json()["request"]

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

        if result.get("status") == 1:
            return result["request"]

    raise TimeoutError("Challenge solve timed out")


def extract_turnstile_sitekey(url):
    """Fetch page and extract Turnstile sitekey."""
    import re
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
    }
    response = requests.get(url, headers=headers, timeout=15)
    match = re.search(r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', response.text)
    return match.group(1) if match else None

Versi Node.js

const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveWAFChallenge(url, challengeType) {
  const method =
    challengeType === "js_challenge" ? "cloudflare_challenge" : "turnstile";
  const sitekey =
    challengeType === "js_challenge" ? "managed" : await extractSitekey(url);

  const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method,
      sitekey,
      pageurl: url,
      json: 1,
    },
  });

  const taskId = submit.data.request;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId, json: 1 },
    });

    if (result.data.status === 1) {
      return result.data.request;
    }
  }

  throw new Error("Challenge solve timed out");
}

async function extractSitekey(url) {
  const response = await axios.get(url, {
    headers: {
      "User-Agent": "Mozilla/5.0 Chrome/120.0.0.0",
    },
  });
  const match = response.data.match(/data-sitekey=["']([0-9x][A-Za-z0-9_-]+)["']/);
  return match ? match[1] : null;
}

Saat operator situs mengubah aturan WAF

Perubahan aturan WAF meninggalkan jejak yang terbaca dari sisi klien:

Perubahan Efek pada otomatisasi Cara mendeteksi
Aturan ditambahkan Challenge baru muncul pada path yang sebelumnya berhasil Pantau perubahan status 503/403
Aturan dihapus Challenge menghilang 200 di mana sebelumnya 503
Tindakan ditingkatkan (Managed → Block) Challenge yang bisa di-solve menjadi hard block 403 bukan 503
Tindakan dilonggarkan (Block → Managed) Hard block menjadi challenge yang bisa di-solve 503 dengan halaman challenge
Threshold diubah (bot score 30 → 50) Lebih banyak request di-challenge Frekuensi challenge meningkat
Cakupan path diubah URL berbeda terpengaruh Path baru mengembalikan challenge

Memantau perubahan secara otomatis

Pemantauan berkala mencatat pergeseran status per URL:

import requests
import time

def monitor_cloudflare_protection(urls, interval=3600):
    """Monitor protection changes across URLs."""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
        "Accept": "text/html,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
    }

    last_status = {}

    while True:
        for url in urls:
            try:
                response = requests.get(
                    url, headers=headers, timeout=15, allow_redirects=False
                )
                status = response.status_code
                has_challenge = status == 503 or "cf-turnstile" in response.text

                current = {"status": status, "challenge": has_challenge}
                previous = last_status.get(url)

                if previous and current != previous:
                    print(f"[CHANGE] {url}")
                    print(f"  Before: {previous}")
                    print(f"  After:  {current}")

                last_status[url] = current

            except requests.RequestException as e:
                print(f"[ERROR] {url}: {e}")

        time.sleep(interval)

Diagnosa cepat dari gejala challenge

Tabel ini memetakan gejala ke kemungkinan aturannya:

Gejala Kemungkinan Aturan WAF Solusi
Challenge hanya di /login Aturan berbasis path Solve challenge untuk path tersebut
Challenge hanya dari IP datacenter Bot score atau aturan reputasi IP Gunakan egress jaringan yang diotorisasi atau solve challenge
Challenge berbeda per negara Aturan berbasis negara Gunakan proxy di negara yang diizinkan atau solve
Challenge setelah N request Aturan berbasis rate Kurangi rate request atau solve setiap challenge
Challenge selalu JS (bukan Turnstile) Tindakan JS Challenge (bukan Managed) Gunakan metode cloudflare_challenge
403 tanpa challenge Tindakan Block (tidak bisa di-solve) Ubah IP, header, atau pola request

Pertanyaan yang sering diajukan

Kenapa request otomatis saya kena challenge padahal browser biasa lolos?

Biasanya karena aturan bot score atau reputasi IP: request dari IP pusat data tanpa sinyal browser lengkap mendapat skor rendah. Solver CaptchaAI menyelesaikannya di browser nyata.

Metode CaptchaAI mana untuk Managed Challenge dan mana untuk JS Challenge?

Pakai turnstile saat Managed Challenge dirender sebagai Turnstile, dan cloudflare_challenge saat yang muncul halaman JavaScript challenge 5 detik.

Apakah semua paket Cloudflare bisa memasang aturan WAF kustom?

Aturan WAF kustom tersedia di semua paket berbayar Cloudflare; paket gratis hanya punya aturan terbatas. Namun Managed Challenge aktif di semua paket, termasuk Free.

Apakah status 403 dari tindakan Block bisa di-solve?

Tidak. 403 dari Block adalah penolakan penuh tanpa challenge — beda dengan 403 dari Interactive Challenge legacy yang masih menampilkan CAPTCHA. Untuk hard block, ubah IP, header, atau pola request.

Berapa lama akses tetap lolos setelah challenge di-solve?

Cookie qa_validation_cookie membuat request berikutnya lewat tanpa challenge selama sekitar 30 menit. Cookie terikat pada IP dan User-Agent, jadi menggantinya memicu challenge lagi.


Ringkasan

Poin utama:

  • CAPTCHA Cloudflare dipicu oleh bot score, negara, path, header, atau rate request.
  • Tindakan tersering, Managed Challenge, berubah adaptif menjadi invisible challenge, Turnstile, atau halaman JS.
  • Selesaikan status 503 dengan CaptchaAI memakai turnstile atau cloudflare_challenge.
  • Hard block 403 bukan untuk di-solve — ubah IP atau pola request Anda.

Artikel Terkait

Komentar dinonaktifkan untuk artikel ini.