Integrasikan Web Application Firewall (WAF) App Protection SDK ke dalam aplikasi iOS Anda untuk mengaktifkan penandatanganan permintaan dan perlindungan bot. Setelah integrasi, WAF memverifikasi tanda tangan permintaan guna mendeteksi dan memblokir lalu lintas berbahaya.
Prasyarat
Sebelum memulai, pastikan Anda telah memiliki:
Aplikasi iOS yang menjalankan iOS 9.0 atau versi lebih baru (versi sebelumnya tidak didukung)
WAF App Protection SDK untuk iOS — ajukan ticket untuk mendapatkannya dari ahli teknis produk
Kunci otentikasi SDK (AppKey) — setelah Anda mengaktifkan Bot Management, buka Bot Management > App Protection, lalu klik Obtain and Copy AppKey di daftar aplikasi

Paket SDK diberi nama tigertally-X.Y.Z-xxxx-ios.zip (di mana X.Y.Z adalah nomor versi) dan berisi dua file framework serta dua file xcframework. Setiap Akun Alibaba Cloud memiliki satu AppKey yang berlaku untuk semua domain yang dilindungi WAF dan dapat digunakan lintas integrasi Android, iOS, dan HarmonyOS.Contoh AppKey: **OpKLvM6zliu6KopyHIhmneb_u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK**
Pilih versi SDK
SDK iOS tersedia dalam dua versi. Pilih berdasarkan apakah aplikasi Anda menggunakan identifier for advertisers (IDFA):
| Versi | Framework file | Gunakan saat |
|---|---|---|
| IDFA | AliTigerTally_IDFA.framework | Aplikasi Anda menggunakan IDFA |
| Non-IDFA | AliTigerTally_NOIDFA.framework | Aplikasi Anda tidak menggunakan IDFA |
Langkah 1: Buat proyek
Buat proyek iOS baru di Xcode dan selesaikan wizard pengaturan.

Langkah 2: Tambahkan file framework
Tambahkan framework utama SDK ke proyek Anda. Pilih versi yang sesuai dengan kebutuhan IDFA Anda.
Versi IDFA:

Versi Non-IDFA:

Tambahkan framework modul CAPTCHA AliCaptcha.framework ke proyek Anda:

Tambahkan bundle resource AliCaptcha.bundle ke proyek Anda:

Langkah 3: Tambahkan pustaka dependensi
Tambahkan pustaka berikut di bawah Link Binary With Libraries pada build phases target Anda:
| Pustaka | Versi IDFA | Versi Non-IDFA |
|---|---|---|
libc++.tbd | Ya | Ya |
libresolv.9.tbd | Ya | Ya |
CoreTelephony.framework | Ya | Ya |
AdSupport.framework | Ya | Tidak |
AppTrackingTransparency.framework | Ya | Tidak |

Langkah 4: Konfigurasi flag linker
Di Build Settings, tambahkan -ObjC ke Other Linker Flags:

Langkah 5: Tambahkan kode integrasi
Impor file header
Versi IDFA:
#import <AliTigerTally_IDFA/AliTigerTally.h>Versi Non-IDFA:
#import <AliTigerTally_NOIDFA/AliTigerTally.h>Inisialisasi SDK
Panggil init sekali saat aplikasi dimulai untuk mengumpulkan informasi perangkat. SDK mendukung tiga mode pengumpulan data — pilih salah satu berdasarkan kebutuhan privasi Anda:
| Mode | collectType nilai | Data yang dikumpulkan |
|---|---|---|
| Lengkap | TT_DEFAULT | Semua data perangkat |
| Privasi kustom | TT_NO_BASIC_DATA, TT_NO_UNIQUE_DATA, TT_NO_EXTRA_DATA (dapat dikombinasikan dengan |) | Data parsial — mengecualikan kategori yang ditentukan |
| Tanpa privasi | TT_NOT_GRANTED | Tidak ada bidang privasi (mengecualikan IDFA dan identifier for vendors (IDFV)) |
Data lengkap meningkatkan deteksi ancaman. Pilih mode yang memenuhi persyaratan kepatuhan privasi Anda sekaligus mengumpulkan data sebanyak mungkin.
Referensi kategori pengumpulan data:
collectType flag | Mengecualikan |
|---|---|
TT_NO_BASIC_DATA | Nama perangkat, versi sistem, resolusi layar |
TT_NO_UNIQUE_DATA | IDFV dan IDFA |
TT_NO_EXTRA_DATA | Informasi Wi-Fi terhubung (SSID, BSSID) dan jaringan Wi-Fi terdekat |
Signature metode init:
- (int)init:(NSString *)appkey
collectType:(TTCollectType)type
options:(NSMutableDictionary *_Nullable)options
listener:(TTInitListener _Nullable)onInitFinish;Parameter:
| Parameter | Tipe | Deskripsi |
|---|---|---|
appkey | NSString | AppKey Anda dari konsol Bot Management |
collectType | TTCollectType | Mode pengumpulan data (lihat tabel di atas) |
options | NSMutableDictionary | Konfigurasi pelaporan opsional (lihat tabel di bawah). Default: nil |
onInitFinish | TTInitListener | Callback inisialisasi. Default: nil |
Konfigurasi options:
| Kunci | Nilai | Deskripsi |
|---|---|---|
IPv6 | 0 (default), 1 | 0: gunakan IPv4. 1: gunakan IPv6. |
Intl | 0 (default), 1 | 0: laporkan dari Tiongkok daratan. 1: laporkan dari luar Tiongkok daratan. |
CustomUrl | String URL | URL server pelaporan kustom (untuk wilayah tertentu, misalnya https://cloudauth-device.us-west-1.aliyuncs.com) |
CustomHost | String hostname | Host server pelaporan kustom (misalnya cloudauth-device.us-west-1.aliyuncs.com) |
Untuk sebagian besar wilayah internasional, aturIntlke1. AturCustomUrldanCustomHosthanya jika Anda perlu melaporkan ke wilayah tertentu, seperti AS (Silicon Valley).
Nilai kembalian: int — 0 jika berhasil, negatif jika gagal.
Contoh:
NSString *appKey = @"<your-appkey>";
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
[options setValue:@"0" forKey:@"IPv6"]; // Gunakan IPv4
[options setValue:@"0" forKey:@"Intl"]; // Laporkan dari Tiongkok daratan
// [options setValue:@"1" forKey:@"Intl"]; // Laporkan dari luar Tiongkok daratan
// Untuk melaporkan ke AS (Silicon Valley):
// [options setValue:@"https://cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomUrl"];
// [options setValue:@"cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomHost"];
// Pengumpulan data lengkap
if (0 == [[AliTigerTally sharedInstance] init:appKey collectType:TT_DEFAULT options:options listener:nil]) {
NSLog(@"Inisialisasi berhasil");
} else {
NSLog(@"Inisialisasi gagal");
}
// Privasi kustom: mengecualikan data perangkat dasar dan tambahan
TTCollectType collectPrivacy = TT_NO_BASIC_DATA | TT_NO_EXTRA_DATA;
[[AliTigerTally sharedInstance] init:appKey collectType:collectPrivacy options:options listener:nil];
// Tanpa privasi: mengecualikan semua bidang privasi
[[AliTigerTally sharedInstance] init:appKey collectType:TT_NOT_GRANTED options:options listener:nil];Tunggu minimal 2 detik setelah memanggil init sebelum memanggil vmpSign. Interval ini merupakan rekomendasi, bukan persyaratan wajib, dan bertujuan untuk meningkatkan efektivitas perlindungan SDK. Anda dapat menyesuaikan interval ini sesuai kebutuhan, tetapi interval yang lebih pendek dapat mengurangi kualitas perlindungan.
Tetapkan akun pengguna (opsional)
Panggil setAccount untuk mengaitkan pengenal pengguna yang telah didesensitisasi dengan permintaan. Hal ini memungkinkan kebijakan mitigasi WAF yang lebih granular.
/**
* Menetapkan akun pengguna.
* @param account String yang telah didesensitisasi untuk mengidentifikasi pengguna.
*/
- (void)setAccount:(NSString *)account;Untuk pengguna tamu, lewati pemanggilan ini dan lanjutkan dengan inisialisasi. Setelah pengguna masuk, panggil setAccount, lalu inisialisasi ulang:
[[AliTigerTally sharedInstance] setAccount:@"<desensitized-user-id>"];Tandatangani permintaan
Semua contoh menggunakan vmpSign untuk menghasilkan string wtoken dan melampirkannya ke header permintaan wToken.
Penandatanganan default
Gunakan penandatanganan default jika Anda belum memilih opsi penandatanganan kustom di konsol.
Berikan badan permintaan ke vmpSign. Untuk permintaan tanpa badan (seperti GET), berikan nil atau string kosong yang diencode sebagai NSData.
/**
* Menandatangani data menggunakan teknologi VMP.
* @param input Data yang akan ditandatangani (badan permintaan, atau nil untuk badan kosong).
* @return String wtoken untuk otentikasi permintaan.
*/
- (NSString *)vmpSign:(NSData *)input;Contoh:
NSString *body = @"hello world";
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// Lampirkan wtoken ke header permintaan HTTP "wToken"Penandatanganan kustom
Gunakan penandatanganan kustom jika Anda telah memilih opsi penandatanganan kustom di konsol. Panggil vmpHash terlebih dahulu untuk menghasilkan whash, lalu berikan nilai whash sebagai input ke vmpSign.
Untuk permintaan POST, PUT, PATCH: berikan badan permintaan ke
vmpHashUntuk permintaan GET, DELETE: berikan URL lengkap ke
vmpHash
Tambahkan nilai whash ke header permintaan ali_sign_whash. Juga atur Custom Signing Field ke ali_sign_whash dalam konfigurasi kebijakan berbasis skenario di konsol Anda.
/**
* Meng-hash data untuk penandatanganan kustom.
* @param type Metode HTTP (TT_GET, TT_POST, TT_PUT, TT_PATCH, TT_DELETE).
* @param input Data yang akan di-hash (badan permintaan untuk POST/PUT/PATCH; URL lengkap untuk GET/DELETE).
* @return String whash.
*/
- (NSString *)vmpHash:(TTRequestType)type input:(NSData *)input;Contoh permintaan POST:
NSString *body = @"hello world";
// Hasilkan whash dari badan permintaan
NSString *whash = [[AliTigerTally sharedInstance]
vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]];
// Hasilkan wtoken dari whash
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
// Lampirkan whash ke header "ali_sign_whash" dan wtoken ke header "wToken"
NSLog(@"whash: %@, wtoken: %@", whash, wtoken);Contoh permintaan GET:
NSString *url = @"https://tigertally.aliyun.com/apptest";
// Hasilkan whash dari URL lengkap
NSString *whash = [[AliTigerTally sharedInstance]
vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]];
// Hasilkan wtoken dari whash
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];URL yang diberikan kevmpHashharus persis sama dengan URL permintaan akhir. Beberapa framework secara otomatis mengencode karakter Tionghoa atau parameter kueri — verifikasi bahwa tidak ada perbedaan encoding.vmpHashtidak menerima string kosong; untuk permintaan GET, URL harus mencakup path atau parameter kueri.
Tangani verifikasi sekunder
Jika WAF mendeteksi aktivitas mencurigakan, sistem akan mengembalikan tantangan CAPTCHA. Periksa setiap respons dan tampilkan slider CAPTCHA bila diperlukan.
Periksa respons
Berikan cookie respons dan badan respons ke cptCheck setelah setiap permintaan:
/**
* Memeriksa apakah verifikasi sekunder diperlukan.
* @param cookie Semua cookie dari respons (digabung dalam format "key=value;").
* @param body Badan respons lengkap.
* @return 0: lolos. 1: verifikasi sekunder diperlukan.
*/
- (int)cptCheck:(NSString *)cookie body:(NSData *)body;Header respons mungkin mencakup beberapa bidangSet-Cookie. Gabungkan menjadi formatkey1=value1;key2=value2;sebelum diberikan kecptCheck.
Contoh:
NSString *cookie = @"key1=value1;key2=value2;";
NSData *body = /* badan respons */;
int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
// recheck == 1: tampilkan slider CAPTCHATampilkan slider CAPTCHA
Saat cptCheck mengembalikan 1, buat dan tampilkan slider TTCaptcha:
/**
* Membuat slider CAPTCHA untuk verifikasi sekunder.
* @param view Tampilan induk.
* @param option Konfigurasi slider.
* @param delegate Callback untuk hasil verifikasi.
* @return Objek slider TTCaptcha.
*/
- (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate;Konfigurasi TTOption:
| Properti | Tipe | Deskripsi |
|---|---|---|
cancelable | BOOL | Izinkan pengguna menutup slider dengan mengetuk di luar area slider |
customUri | NSString | Path ke halaman verifikasi kustom (file HTML lokal atau URL remote) |
language | NSString | Bahasa tampilan (misalnya @"cn" untuk Tionghoa, @"en" untuk Inggris) |
Callback TTDelegate:
@protocol TTDelegate <NSObject>
@required
// Dipanggil saat verifikasi berhasil.
- (void)success:(TTCaptcha *)captcha data:(NSString *)data;
// Dipanggil saat verifikasi gagal.
- (void)failed:(TTCaptcha *)captcha code:(NSString *)code;
@endContoh:
TTOption *option = [[TTOption alloc] init];
option.language = @"cn";
option.cancelable = YES;
TTCaptcha *captcha = [[AliTigerTally sharedInstance]
cptCreate:[self view] option:option delegate:self];
[captcha show];
// Di callback TTDelegate:
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"Verifikasi berhasil: %@", data);
}
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"Verifikasi gagal dengan kode: %@", code);
}Kode status inisialisasi
| Kode | Konstanta | Deskripsi |
|---|---|---|
0 | TT_SUCCESS | Inisialisasi berhasil |
-1 | TT_NOT_INIT | init belum dipanggil |
-2 | TT_NOT_PERMISSION | Izin iOS yang diperlukan belum diberikan |
-3 | TT_UNKNOWN_ERROR | Kesalahan sistem tidak dikenal |
-4 | TT_NETWORK_ERROR | Kesalahan jaringan |
-5 | TT_NETWORK_ERROR_EMPTY | Kesalahan jaringan — badan respons kosong |
-6 | TT_NETWORK_ERROR_INVALID | Kesalahan jaringan — format respons tidak valid |
-7 | TT_PARSE_SRV_CFG_ERROR | Gagal mengurai konfigurasi server |
-8 | TT_NETWORK_RET_CODE_ERROR | Gateway mengembalikan respons kegagalan |
-9 | TT_APPKEY_EMPTY | AppKey kosong |
-10 | TT_PARAMS_ERROR | Kesalahan parameter |
-11 | TT_FGKEY_ERROR | Kesalahan perhitungan kunci |
-12 | TT_APPKEY_ERROR | Versi SDK tidak cocok dengan versi AppKey |
Kode kesalahan CAPTCHA
| Kode | Deskripsi |
|---|---|
1001 | Verifikasi gagal (terjadi pengecualian slider selama atau setelah interaksi pengguna) |
1002 | Pengecualian sistem |
1003 | Kesalahan parameter |
1005 | Verifikasi dibatalkan oleh pengguna |
8001 | Gagal memanggil slider |
8002 | Data verifikasi slider tidak normal |
8003 | Pengecualian internal selama verifikasi slider |
8004 | Kesalahan jaringan |
Pemecahan masalah
vmpSign atau vmpHash mengembalikan string kesalahan alih-alih token
Jika whash atau wtoken berisi salah satu string berikut, terjadi kesalahan:
"you must call init first"—initbelum dipanggil sebelum penandatanganan. Panggilinitdan tunggu minimal 2 detik sebelum memanggilvmpSign."you must input correct data"— Data input tidak valid. Periksa bahwa badan permintaan atau URL tidak kosong atau rusak."you must input correct type"— Tipe metode HTTP yang diberikan kevmpHashsalah.
Ketidaksesuaian encoding URL dalam penandatanganan permintaan GET
Jika whash untuk permintaan GET ditolak, URL yang diberikan ke vmpHash mungkin tidak sesuai dengan URL permintaan aktual. Beberapa framework jaringan secara otomatis melakukan percent-encoding pada parameter kueri atau karakter Tionghoa. Catat kedua URL tersebut dan bandingkan karakter per karakter.
Inisialisasi gagal dengan TT_NETWORK_ERROR (-4)
Periksa apakah perangkat memiliki akses jaringan dan domain pelaporan dapat dijangkau. Jika Anda melakukan deployment di luar Tiongkok daratan, atur Intl ke 1 dalam dictionary options. Untuk titik akhir wilayah tertentu, atur CustomUrl dan CustomHost.
Contoh lengkap
Contoh berikut menunjukkan alur integrasi lengkap — inisialisasi, penandatanganan permintaan, pemeriksaan verifikasi sekunder, dan tampilan CAPTCHA.
#import "DemoController.h"
#if __has_include(<AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>)
#import <AliTigerTally_NOIDFA/AliTigerTally_NOIDFA.h>
#else
#import <AliTigerTally_IDFA/AliTigerTally_IDFA.h>
#endif
@interface DemoController () <TTDelegate>
@end
static NSString *kAppHost = @"******";
static NSString *kAppUrl = @"******";
static NSString *kAppkey = @"******";
@implementation DemoController
- (void)viewDidLoad {
[super viewDidLoad];
[self doTest];
}
- (void)doTest {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
// Langkah 1: Inisialisasi SDK
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
// [options setValue:@"1" forKey:@"Intl"]; // Hapus komentar untuk luar Tiongkok daratan
int code = [[AliTigerTally sharedInstance] init:kAppkey collectType:TT_DEFAULT options:options listener:nil];
NSLog(@"tigertally init: %d", code);
// Langkah 2: Tunggu minimal 2 detik sebelum menandatangani
[NSThread sleepForTimeInterval:2.0];
// Langkah 3: Tandatangani permintaan (contoh penandatanganan kustom)
NSString *body = @"hello world";
NSString *whash = [[AliTigerTally sharedInstance]
vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]];
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"tigertally vmp — whash: %@, wtoken: %@", whash, wtoken);
// Untuk penandatanganan default (opsi penandatanganan kustom tidak dipilih):
// wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// Langkah 4: Kirim permintaan dan periksa verifikasi sekunder
[self doPost:kAppUrl host:kAppHost whash:whash wtoken:wtoken
body:[body dataUsingEncoding:NSUTF8StringEncoding]
callback:^(NSInteger code, NSString *cookie, NSData *body) {
int check = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
NSLog(@"hasil pemeriksaan captcha: %d", check);
if (check == 0) return;
// Verifikasi sekunder diperlukan — tampilkan CAPTCHA di thread utama
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self doShow];
}];
}];
}];
[thread start];
}
- (void)doShow {
TTOption *option = [[TTOption alloc] init];
option.language = @"cn";
option.cancelable = YES;
TTCaptcha *captcha = [[AliTigerTally sharedInstance]
cptCreate:[self view] option:option delegate:self];
[captcha show];
}
- (void)doPost:(NSString *)url host:(NSString *)host
whash:(NSString *)whash wtoken:(NSString *)wtoken
body:(NSData *)body callback:(void(^)(NSInteger code, NSString *cookie, NSData *body))callback {
NSURL *requestUrl = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestUrl
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[request setValue:@"text/x-markdown" forHTTPHeaderField:@"Content-Type"];
[request setValue:host forHTTPHeaderField:@"HOST"];
[request setValue:wtoken forHTTPHeaderField:@"wToken"];
if (whash) {
[request setValue:whash forHTTPHeaderField:@"ali_sign_whash"];
}
request.HTTPMethod = @"POST";
request.HTTPBody = body;
NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
callback(-1, nil, [[error description] dataUsingEncoding:NSUTF8StringEncoding]);
return;
}
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSMutableString *cookies = [[NSMutableString alloc] init];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if ([url containsString:[cookie domain]]) {
[cookies appendFormat:@"%@=%@;", [cookie name], [cookie value]];
}
}
callback(httpResponse.statusCode, cookies, data);
}];
[dataTask resume];
}
#pragma mark - TTDelegate
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"captcha gagal: %@", code);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
message:code preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"captcha berhasil: %@", data);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
message:data preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
@end