All Products
Search
Document Center

Web Application Firewall:Integrasikan SDK Android

Last Updated:Apr 01, 2026

Untuk menerapkan aturan anti-crawler pada aplikasi Android Anda, integrasikan terlebih dahulu kit pengembangan perangkat lunak (SDK) Perlindungan aplikasi. Topik ini memandu Anda melalui langkah-langkah yang diperlukan.

Latar Belakang

SDK Perlindungan aplikasi menandatangani setiap permintaan yang dikirim oleh aplikasi Anda. WAF memverifikasi tanda tangan tersebut untuk membedakan lalu lintas sah dari bot dan aktor jahat lainnya, sehingga melindungi layanan backend aplikasi Anda.

Batasan

  • Arsitektur ABI Android yang didukung: arm64-v8a dan armeabi-v7a

  • Tingkat API Android minimum: 16

  • Setelah memanggil init, tunggu minimal 2 detik sebelum memanggil vmpSign. SDK memerlukan waktu ini untuk menginisialisasi sepenuhnya kemampuan keamanannya. Penundaan ini bersifat rekomendasi dan dapat disesuaikan sesuai kebutuhan. Penundaan yang lebih singkat dapat mengurangi efektivitas perlindungan.

Prasyarat

Sebelum memulai, pastikan Anda telah memiliki:

  • Paket SDK untuk aplikasi Android (tigertally-X.Y.Z-xxxxxx-android.tgz), yang mencakup AliTigerTally_X.Y.Z.aar dan AliCaptcha_X.Y.Z.aar. Untuk mendapatkan SDK, ajukan ticket.

  • Kunci otentikasi SDK (appkey). Setelah Anda mengaktifkan Bot Management, buka Bot Management > App Protection dan klik Obtain and Copy AppKey.

image
Setiap Akun Alibaba Cloud memiliki satu appkey unik yang berlaku untuk semua domain yang dilindungi WAF dan berfungsi lintas integrasi aplikasi Android, iOS, dan HarmonyOS. Contoh format appkey: **OpKLvM6zliu6KopyHIhmneb_u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK**

Langkah 1: Buat proyek

Buka Android Studio dan buat proyek Android menggunakan wizard konfigurasi. Struktur direktori proyek akan tampak seperti berikut:

image.png

Langkah 2: Integrasikan paket AAR

  1. Ekstrak tigertally-X.Y.Z-xxxxxx-android.tgz dan salin semua file AAR dari folder hasil ekstraksi ke direktori libs modul utama Anda. Jalur pastinya mungkin berbeda tergantung konfigurasi proyek Anda.

    image.png

  2. Buka build.gradle untuk modul aplikasi Anda. Tambahkan direktori libs sebagai sumber dependensi lokal dan deklarasikan dependensi kompilasi untuk kedua file AAR serta pustaka pihak ketiga yang diperlukan:

    Penting

    Ganti X.Y.Z dalam nama file AAR dengan nomor versi aktual dari paket SDK.

    dependencies {
        // ...
        implementation files('libs/AliTigerTally_X.Y.Z.aar')
        implementation files('libs/AliCaptcha_X.Y.Z.aar')
    
        // Pustaka pihak ketiga yang diperlukan
        implementation 'com.alibaba:fastjson:1.2.83_noneautotype'
        implementation 'com.squareup.okhttp3:okhttp:3.11.0'
        implementation 'com.squareup.okio:okio:1.14.0'
    }
  3. Jika Anda menggunakan ProGuard untuk obfuscasi kode, tambahkan aturan -keep berikut ke file konfigurasi ProGuard Anda untuk mempertahankan metode API SDK:

    -keep class com.aliyun.TigerTally.** {*;}
    -keep class com.aliyun.captcha.* {*;}
    -keepclassmembers,allowobfuscation class * {
         @com.alibaba.fastjson.annotation.JSONField <fields>;
    }
    -keep class com.alibaba.fastjson.** {*;}

Langkah 3: Filter arsitektur CPU SO

Jika proyek Anda belum menyertakan file SO, tambahkan filter ABI berikut ke file build.gradle Anda:

android {
    defaultConfig {
        ndk {
            abiFilters 'arm64-v8a', 'armeabi-v7a'
        }
    }
}

Langkah 4: Deklarasikan izin aplikasi

Tambahkan izin berikut ke AndroidManifest.xml Anda.

Wajib:

<uses-permission android:name="android.permission.INTERNET"/>

Opsional — berikan izin ini untuk meningkatkan akurasi pengumpulan sinyal perangkat:

IzinJenis pemberianDeskripsi
android.permission.BLUETOOTHWaktu instalasiMengumpulkan status Bluetooth untuk sidik jari perangkat.
android.permission.READ_PHONE_STATEWaktu instalasiMembaca pengenal perangkat seperti IMEI.
android.permission.ACCESS_WIFI_STATEWaktu instalasiMembaca detail koneksi Wi-Fi (SSID, BSSID).
android.permission.ACCESS_NETWORK_STATEWaktu instalasiMembaca status konektivitas jaringan.
android.permission.READ_EXTERNAL_STORAGERuntime (Android 6.0+)Membaca penyimpanan perangkat untuk pengumpulan sinyal.
android.permission.WRITE_EXTERNAL_STORAGERuntime (Android 6.0+)Menulis ke penyimpanan perangkat untuk pengumpulan sinyal.

Izin waktu instalasi diberikan secara otomatis saat instalasi. Izin runtime pada Android 6.0 dan versi lebih baru harus diminta secara dinamis saat runtime; tidak diberikan otomatis saat instalasi.

Langkah 5: Tambahkan kode integrasi

Tambahkan import

import com.alibaba.fastjson.*;
import com.aliyun.tigertally.*;

Atur penandatanganan data

Lakukan langkah-langkah berikut secara berurutan: atur pengenal akun pengguna, inisialisasi SDK, lalu tandatangani permintaan.

1. Atur akun pengguna

Tetapkan pengenal pengguna akhir kustom agar Anda dapat menerapkan kebijakan WAF yang ditargetkan per pengguna.

public static int setAccount(String account)
ParameterTipeWajibDeskripsi
accountStringTidakString yang mengidentifikasi pengguna. Gunakan format yang telah didesensitisasi.

Nilai kembali: 0 jika berhasil, -1 jika gagal.

Kode contoh:

// Untuk pengguna tamu, lewati setAccount dan langsung inisialisasi.
// Setelah pengguna masuk, panggil setAccount dan inisialisasi ulang.
String account = "user001";
TigerTallyAPI.setAccount(account);

2. Inisialisasi SDK

Panggil init sekali untuk mengumpulkan informasi perangkat. Anda dapat memanggilnya lagi nanti untuk memulai siklus pengumpulan baru untuk konteks bisnis yang berbeda.

public static int init(Context context, String appkey, int collectType,
                       Map<String, String> otherOptions, TTInitListener listener);

Parameter:

ParameterTipeWajibBawaanDeskripsi
contextContextYaKonteks aplikasi Anda.
appkeyStringYaKunci otentikasi SDK.
collectTypeintYaMode pengumpulan data. Lihat tabel di bawah.
otherOptionsMap\<String, String\>TidaknullOpsi tambahan.
listenerTTInitListenerTidaknullCallback untuk hasil inisialisasi.

Nilai `collectType`:

NilaiDeskripsi
TT_DEFAULTMengumpulkan semua data.
TT_NO_BASIC_DATAMengecualikan data perangkat dasar: Nama perangkat, versi Android, dan resolusi layar.
TT_NO_IDENTIFY_DATAMengecualikan pengenal perangkat: IMEI, IMSI, SimSerial, BuildSerial (SN), dan alamat MAC.
TT_NO_UNIQUE_DATAMengecualikan pengenal unik: Open Anonymous Device Identifier (OAID), Google Advertising ID, dan ID Android.
TT_NO_EXTRA_DATAMengecualikan data perangkat tambahan: daftar aplikasi berbahaya/abu-abu, IP LAN, IP DNS, detail Wi-Fi (SSID, BSSID), daftar Wi-Fi terdekat, lokasi, dan data sensor.
TT_NOT_GRANTEDMengecualikan semua data privasi di atas.

Gabungkan beberapa flag pengecualian dengan |. Misalnya, TT_NO_BASIC_DATA | TT_NO_UNIQUE_DATA mengecualikan kedua kategori tersebut. Pilih mode yang memenuhi persyaratan kepatuhan Anda sekaligus mempertahankan cukup data untuk pendeteksian bot yang efektif.

Nilai `otherOptions`:

KunciDeskripsiBawaanWajib
IPv60: Gunakan IPv4. 1: Gunakan IPv6.0Tidak
Intl0: Laporkan ke Tiongkok daratan. 1: Laporkan luar Tiongkok daratan. Atur ke 1 untuk instans WAF di luar Tiongkok daratan; jika tidak, gunakan nilai bawaan 0.0Tidak
CustomUrlNama domain server pelaporan data. Contoh: https://cloudauth-device.us-west-1.aliyuncs.comTidak
CustomHostHost server pelaporan data. Contoh: cloudauth-device.us-west-1.aliyuncs.comTidak
Untuk sebagian besar wilayah internasional, mengatur Intl sudah cukup. Gunakan CustomUrl dan CustomHost hanya saat melaporkan ke titik akhir tertentu, seperti situs US (West).

Callback inisialisasi:

public interface TTInitListener {
    // code: kode status pemanggilan inisialisasi
    void onInitFinish(int code);
}

Kode status callback (`TTCode`):

KodeNilaiDeskripsi
TT_SUCCESS0SDK berhasil diinisialisasi.
TT_NOT_INIT-1SDK belum diinisialisasi.
TT_NOT_PERMISSION-2Izin Android yang diperlukan belum diberikan sepenuhnya.
TT_UNKNOWN_ERROR-3Kesalahan sistem tidak dikenal.
TT_NETWORK_ERROR-4Kesalahan jaringan.
TT_NETWORK_ERROR_EMPTY-5Kesalahan jaringan — badan respons kosong.
TT_NETWORK_ERROR_INVALID-6Kesalahan jaringan — format respons tidak valid.
TT_PARSE_SRV_CFG_ERROR-7Gagal mengurai konfigurasi server.
TT_NETWORK_RET_CODE_ERROR-8Gateway tidak mengembalikan nilai.
TT_APPKEY_EMPTY-9Appkey kosong.
TT_PARAMS_ERROR-10Kesalahan parameter.
TT_FGKEY_ERROR-11Kesalahan perhitungan kunci.
TT_APPKEY_ERROR-12Versi SDK tidak cocok dengan versi appkey.

Nilai kembali: 0 jika berhasil, -1 jika gagal.

Kode contoh:

// Ganti dengan appkey Anda yang sebenarnya.
final String appkey = "******";

Map<String, String> options = new HashMap<>();
options.put("IPv6", "0");   // Gunakan IPv4
options.put("Intl", "1");   // Laporkan luar Tiongkok daratan

// Untuk melaporkan secara spesifik ke situs US (West):
// options.put("CustomUrl", "https://cloudauth-device.us-west-1.aliyuncs.com");
// options.put("CustomHost", "cloudauth-device.us-west-1.aliyuncs.com");

// Pengumpulan data lengkap
int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_DEFAULT, options, null);

// Pengecualian privasi selektif — gabungkan flag dengan |
int privacyFlag = TigerTallyAPI.TT_NO_BASIC_DATA | TigerTallyAPI.TT_NO_UNIQUE_DATA;
int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, privacyFlag, options, null);

// Mengecualikan semua data privasi
int ret = TigerTallyAPI.init(this.getApplicationContext(), appkey, TigerTallyAPI.TT_NOT_GRANTED, options, null);

Log.d("AliSDK", "ret:" + ret);

3. Hash data (penandatanganan kustom saja)

Lewati langkah ini jika Anda menggunakan penandatanganan bawaan.

Untuk penandatanganan kustom, panggil vmpHash untuk menghitung hash dari data permintaan. Berikan badan permintaan untuk permintaan POST, PUT, dan PATCH, atau URL lengkap untuk permintaan GET dan DELETE. Kemudian tambahkan string whash yang dikembalikan ke header permintaan HTTP ali_sign_whash.

public enum RequestType { GET, POST, PUT, PATCH, DELETE }

public static String vmpHash(RequestType type, byte[] input);
ParameterTipeWajibDeskripsi
typeRequestTypeYaMetode permintaan HTTP.
inputbyte[]YaData yang akan di-hash. Tidak boleh berupa string kosong. Untuk GET/DELETE, gunakan URL lengkap (termasuk path dan parameter kueri).

Nilai kembali: String whash.

Kode contoh:

// Permintaan GET
String url = "https://tigertally.aliyun.com/apptest";
String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes());
Log.d("AliSDK", "whash:" + whash);

// Permintaan POST
String body = "hello world";
String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes());
Log.d("AliSDK", "whash:" + whash);

4. Tandatangani data

Panggil vmpSign untuk menghasilkan string wtoken guna otentikasi permintaan. Tambahkan wtoken ke header permintaan HTTP wToken.

  • Penandatanganan bawaan: berikan badan permintaan sebagai input.

  • Penandatanganan kustom: berikan whash dari vmpHash sebagai input.

public static String vmpSign(int type, byte[] input);
ParameterTipeWajibDeskripsi
typeintYaJenis tanda tangan. Harus 1.
inputbyte[]YaData yang akan ditandatangani. Untuk penandatanganan bawaan: badan permintaan. Untuk penandatanganan kustom: string whash. Jika badan permintaan kosong, berikan null atau "".getBytes("UTF-8").

Nilai kembali: String wtoken.

Kode contoh:

// Penandatanganan bawaan
String body = "i am the request body, encrypted or not!";
String wtoken = TigerTallyAPI.vmpSign(1, body.getBytes("UTF-8"));
Log.d("AliSDK", "wToken:" + wtoken);

// Penandatanganan kustom — permintaan GET
String url = "https://tigertally.aliyun.com/apptest";
String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.GET, url.getBytes());
String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken);

// Penandatanganan kustom — permintaan POST
String body = "hello world";
String whash = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, body.getBytes());
String wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
Log.d("AliSDK", "whash:" + whash + ", wtoken:" + wtoken);
Penting
  • Untuk penandatanganan kustom, saat Anda mengonfigurasi kebijakan anti-bot spesifik skenario di Konsol, atur Custom Signature Field ke ali_sign_whash.

  • Saat menghitung whash untuk permintaan GET, URL input harus persis sama dengan URL akhir yang digunakan dalam permintaan jaringan. Beberapa framework secara otomatis melakukan encode URL terhadap karakter Tionghoa atau parameter — pertimbangkan hal ini saat memberikan URL.

  • vmpHash tidak menerima string kosong. URL input harus mencakup path atau parameter kueri.

  • Jika vmpHash atau vmpSign mengembalikan salah satu string berikut, terjadi kesalahan inisialisasi:

  • "you must call init first"init belum dipanggil sebelum penandatanganan.

  • "you must input correct data" — data input tidak valid.

  • "you must input correct type" — jenis input tidak valid.

Lakukan otentikasi dua faktor

Jika WAF menentukan bahwa permintaan mencurigakan, WAF dapat memicu tantangan CAPTCHA untuk memverifikasi pengguna. Gunakan metode berikut untuk menangani alur ini.

1. Periksa apakah tantangan diperlukan

Setelah menerima respons dari server Anda, periksa bidang cookie dan body untuk menentukan apakah WAF telah menandai permintaan tersebut. Gabungkan semua entri Set-Cookie dari header respons menjadi satu string cookie sebelum memanggil metode ini.

public static int cptCheck(String cookie, String body)
ParameterTipeWajibDeskripsi
cookieStringYaSemua cookie dari header respons, digabung menjadi satu string.
bodyStringYaBadan respons lengkap.

Nilai kembali: 0 — permintaan lolos; 1 — tantangan CAPTCHA diperlukan.

Kode contoh:

String cookie = "key1=value1;key2=value2;";
String body = "....";
int recheck = TigerTallyAPI.cptCheck(cookie, body);
Log.d("AliSDK", "recheck:" + recheck);

2. Tampilkan slider CAPTCHA

Jika cptCheck mengembalikan 1, buat dan tampilkan slider CAPTCHA kepada pengguna.

public static TTCaptcha cptCreate(Activity activity, TTOption option, TTListener listener);
ParameterTipeWajibDeskripsi
activityActivityYaAktivitas saat ini.
optionTTOptionYaKonfigurasi slider.
listenerTTListenerYaCallback untuk hasil verifikasi.

Nilai kembali: Objek TTCaptcha dengan metode show(), dismiss(), dan getTraceId().

Bidang `TTOption`:

BidangTipeBawaanDeskripsi
cancelablebooleanApakah mengetuk di luar slider akan menutupnya.
customUriStringHalaman CAPTCHA kustom (file HTML lokal atau URL remote).
languageStringBahasa untuk UI slider (misalnya, "cn").

Callback `TTListener`:

public interface TTListener {
    void success(TTCaptcha captcha, String data);  // data = certifyId (token)
    void failed(TTCaptcha captcha, String code);   // code = kode kesalahan
}

Kode kesalahan slider:

KodeDeskripsi
1001Verifikasi gagal.
1002Pengecualian sistem.
1003Kesalahan parameter.
1005Verifikasi dibatalkan oleh pengguna.
8001Kesalahan pemanggilan slider.
8002Data verifikasi slider tidak normal.
8003Pengecualian internal verifikasi slider.
8004Kesalahan jaringan.
Callback failed dipicu saat terdeteksi pengecualian setelah pengguna menyelesaikan gerakan geser, bukan saat pengguna meninggalkan tantangan.

Kode contoh:

TTCaptcha.TTOption option = new TTCaptcha.TTOption();
// option.customUri = "file:///android_asset/ali-tt-captcha-demo.html";
option.language   = "cn";
option.cancelable = false;

TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() {
    @Override
    public void success(TTCaptcha captcha, String data) {
        Log.d(TAG, "captcha check success:" + data);
    }
    @Override
    public void failed(TTCaptcha captcha, String code) {
        Log.d(TAG, "captcha check failed:" + code);
    }
});
captcha.show();

Contoh praktik terbaik

Contoh end-to-end berikut menunjukkan alur integrasi lengkap: inisialisasi, penandatanganan permintaan, pengiriman permintaan, dan penanganan tantangan CAPTCHA.

package com.aliyun.tigertally.apk;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.aliyun.TigerTally.TigerTallyAPI;
import com.aliyun.TigerTally.captcha.api.TTCaptcha;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class DemoActivity extends AppCompatActivity {
    private final static String TAG = "TigerTally-Demo";

    private final static String APP_HOST = "******";
    private final static String APP_URL  = "******";
    private final static String APP_KEY  = "******";

    private final static OkHttpClient okHttpClient = new OkHttpClient();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        doTest();
    }

    private void doTest() {
        Log.d(TAG, "captcha flow");
        new Thread(() -> {
            // Langkah 1: Inisialisasi SDK.
            Map<String, String> options = new HashMap<>();
            // options.put("Intl", "1"); // Hapus komentar untuk pelaporan internasional.
            int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_DEFAULT, options, null);
            // int ret = TigerTallyAPI.init(this, APP_KEY, TigerTallyAPI.TT_NOT_GRANTED, null, null); // Tanpa data privasi
            Log.d(TAG, "tiger tally init: " + ret);

            // Langkah 2: Tunggu minimal 2 detik sebelum menandatangani — inisialisasi bersifat asinkron.
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Langkah 3: Tandatangani data permintaan.
            String data = "hello world";
            String whash = null, wtoken = null;

            // Penandatanganan kustom
            whash  = TigerTallyAPI.vmpHash(TigerTallyAPI.RequestType.POST, data.getBytes());
            wtoken = TigerTallyAPI.vmpSign(1, whash.getBytes());
            Log.d(TAG, "tiger tally vmp: " + whash + ", " + wtoken);

            // Penandatanganan bawaan (tidak perlu vmpHash)
            // wtoken = TigerTallyAPI.vmpSign(1, data.getBytes());
            // Log.d(TAG, "tiger tally vmp: " + wtoken);

            // Langkah 4: Kirim permintaan dan periksa apakah CAPTCHA diperlukan.
            doPost(APP_URL, APP_HOST, whash, wtoken, data, (code, cookie, body) -> {
                int recheck = TigerTallyAPI.cptCheck(cookie, body);
                Log.d(TAG, "captcha check result: " + recheck);

                if (recheck == 0) return;
                this.runOnUiThread(this::doShow);
            });
        }).start();
    }

    // Tampilkan slider CAPTCHA.
    public void doShow() {
        Log.d(TAG, "captcha show");

        TTCaptcha.TTOption option = new TTCaptcha.TTOption();
        // option.customUri = "file:///android_asset/ali-tt-captcha-demo.html";
        option.language   = "cn";
        option.cancelable = false;

        TTCaptcha captcha = TigerTallyAPI.cptCreate(this, option, new TTCaptcha.TTListener() {
            @Override
            public void success(TTCaptcha captcha, String data) {
                Log.d(TAG, "captcha check success:" + data);
            }

            @Override
            public void failed(TTCaptcha captcha, String code) {
                Log.d(TAG, "captcha check failed:" + code);
            }
        });

        captcha.show();
    }

    // Kirim permintaan POST dengan header wToken dan whash opsional.
    public static void doPost(String url, String host, String whash, String wtoken, String body, Callback callback) {
        Log.d(TAG, "start request post");

        int responseCode = 0;
        String responseBody = "";
        StringBuilder responseCookie = new StringBuilder();
        try {
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .addHeader("wToken", wtoken)  // Wajib: token tanda tangan WAF
                    .addHeader("Host",   host)
                    .post(RequestBody.create(MediaType.parse("text/x-markdown"), body.getBytes()));

            if (whash != null) {
                builder.addHeader("ali_sign_whash", whash);  // Wajib untuk penandatanganan kustom
            }
            Response response = okHttpClient.newCall(builder.build()).execute();

            responseCode = response.code();
            responseBody = response.body() == null ? "" : response.body().string();
            for (String item : response.headers("Set-Cookie")) {
                responseCookie.append(item).append(";");
            }

            Log.d(TAG, "response code:" + responseCode);
            Log.d(TAG, "response cookie:" + responseCookie);
            Log.d(TAG, "response body:" + (responseBody.length() > 100 ? responseBody.substring(0, 100) : ""));

            if (response.isSuccessful()) {
                Log.d(TAG, "success: " + response.code() + ", " + response.message());
            } else {
                Log.e(TAG, "failed: " + response.code() + ", " + response.message());
            }

            response.close();
        } catch (Exception e) {
            e.printStackTrace();
            responseCode = -1;
            responseBody = e.toString();
        } finally {
            if (callback != null) {
                callback.onResponse(responseCode, responseCookie.toString(), responseBody);
            }
        }
    }

    public interface Callback {
        void onResponse(int code, String cookie, String body);
    }
}

Setelah SDK diintegrasikan, SDK secara otomatis:

  1. Menandatangani setiap permintaan keluar dengan header wToken untuk diverifikasi oleh WAF.

  2. Secara opsional melampirkan header ali_sign_whash untuk skenario penandatanganan kustom.

  3. Memungkinkan aplikasi Anda mendeteksi dan merespons tantangan CAPTCHA WAF.