Web Application Firewall (WAF) のアプリ保護 SDK をご利用の iOS アプリに統合することで、リクエスト署名とボット保護を有効にできます。統合後、WAF はリクエスト署名を検証し、悪意のあるトラフィックを検出してブロックします。
前提条件
開始する前に、以下をご確認ください。
iOS 9.0 以降を実行している iOS アプリ (それ以前のバージョンはサポートされていません)
iOS 向け WAF アプリ保護 SDK — チケットを起票して、製品技術エキスパートから入手してください
SDK 認証キー (AppKey) — ボット管理を有効化した後、[ボット管理] > [アプリ保護] に移動し、アプリ一覧で [AppKey の取得とコピー] をクリックします

SDK パッケージ名は tigertally-X.Y.Z-xxxx-ios.zip (X.Y.Z はバージョン番号) で、2 つのフレームワークファイルと 2 つの xcframework ファイルが含まれています。各 Alibaba Cloud アカウントには、WAF で保護されたすべてのドメインに適用される 1 つの AppKey があり、Android、iOS、HarmonyOS の統合で共通して使用できます。AppKey の例:**OpKLvM6zliu6KopyHIhmneb_u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK**
SDK バージョンの選択
iOS SDK には 2 つのバージョンがあります。ご利用のアプリが広告主向け識別子 (IDFA) を使用するかに基づいて選択してください。
| バージョン | フレームワークファイル | 使用条件 |
|---|---|---|
| IDFA | AliTigerTally_IDFA.framework | ご利用のアプリが IDFA を使用する場合 |
| Non-IDFA | AliTigerTally_NOIDFA.framework | アプリは IDFA を使用していません |
ステップ 1:プロジェクトの作成
Xcode で新しい iOS プロジェクトを作成し、セットアップウィザードを完了します。

ステップ 2:フレームワークファイルの追加
メインの SDK フレームワークをプロジェクトに追加します。IDFA の要件に合ったバージョンを選択してください。
IDFA バージョン:

Non-IDFA バージョン:

CAPTCHA モジュールフレームワーク AliCaptcha.framework をプロジェクトに追加します。

リソースバンドル AliCaptcha.bundle をプロジェクトに追加します。

ステップ 3:依存ライブラリの追加
ターゲットのビルドフェーズにある [Link Binary With Libraries] に、以下のライブラリを追加します。
| ライブラリ | IDFA バージョン | Non-IDFA バージョン |
|---|---|---|
libc++.tbd | はい | はい |
libresolv.9.tbd | はい | はい |
CoreTelephony.framework | はい | はい |
AdSupport.framework | はい | いいえ |
AppTrackingTransparency.framework | はい | いいえ |

ステップ 4:リンカーフラグの設定
[ビルド設定] で、-ObjC を [その他のリンカフラグ] に追加します:

ステップ 5:統合コードの追加
ヘッダーファイルのインポート
IDFA バージョン:
#import <AliTigerTally_IDFA/AliTigerTally.h>Non-IDFA バージョン:
#import <AliTigerTally_NOIDFA/AliTigerTally.h>SDK の初期化
アプリの起動時に init を一度呼び出して、デバイス情報を収集します。SDK は 3 つのデータ収集モードをサポートしています。プライバシー要件に基づいて 1 つを選択してください。
| モード | collectType の値 | 収集内容 |
|---|---|---|
| フル | TT_DEFAULT | すべてのデバイスデータ |
| カスタムプライバシー | TT_NO_BASIC_DATA、TT_NO_UNIQUE_DATA、TT_NO_EXTRA_DATA (| で組み合わせ可能) | 部分的なデータ — 指定されたカテゴリを除外 |
| 非プライバシー | TT_NOT_GRANTED | プライバシー関連フィールドなし (IDFA とベンダー向け識別子 (IDFV) を除外) |
完全なデータは脅威検知の精度を向上させます。プライバシーコンプライアンスの要件を満たしつつ、できるだけ多くのデータを収集するモードを選択してください。
データ収集カテゴリのリファレンス:
collectType フラグ | 除外対象 |
|---|---|
TT_NO_BASIC_DATA | デバイス名、システムバージョン、画面の解像度 |
TT_NO_UNIQUE_DATA | IDFV と IDFA |
TT_NO_EXTRA_DATA | 接続中の Wi-Fi 情報 (SSID、BSSID) および周辺の Wi-Fi ネットワーク |
`init` メソッドシグネチャ:
- (int)init:(NSString *)appkey
collectType:(TTCollectType)type
options:(NSMutableDictionary *_Nullable)options
listener:(TTInitListener _Nullable)onInitFinish;パラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
appkey | NSString | ボット管理コンソールで取得した AppKey |
collectType | TTCollectType | データ収集モード (上記の表をご参照ください) |
options | NSMutableDictionary | レポート構成 (任意、下記の表をご参照ください)。デフォルト:nil |
onInitFinish | TTInitListener | 初期化コールバック。デフォルト:nil |
`options` の構成:
| キー | 値 | 説明 |
|---|---|---|
IPv6 | 0 (デフォルト)、1 | 0:IPv4 を使用します。1:IPv6 を使用します。 |
Intl | 0 (デフォルト)、1 | 0:中国本土からレポートします。1:中国本土以外からレポートします。 |
CustomUrl | URL 文字列 | カスタムレポートサーバー URL (特定のリージョン向け、例:https://cloudauth-device.us-west-1.aliyuncs.com) |
CustomHost | ホスト名文字列 | カスタムレポートサーバーホスト (例:cloudauth-device.us-west-1.aliyuncs.com) |
ほとんどの国際リージョンでは、Intlを1に設定してください。米国 (シリコンバレー) など、特定のリージョンにレポートする必要がある場合にのみ、CustomUrlとCustomHostを設定します。
戻り値: int — 成功した場合は 0、失敗した場合は負の値を返します。
例:
NSString *appKey = @"<your-appkey>";
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
[options setValue:@"0" forKey:@"IPv6"]; // IPv4 を使用
[options setValue:@"0" forKey:@"Intl"]; // 中国本土からレポート
// [options setValue:@"1" forKey:@"Intl"]; // 中国本土以外からレポート
// 米国 (シリコンバレー) にレポートする場合:
// [options setValue:@"https://cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomUrl"];
// [options setValue:@"cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomHost"];
// 全データ収集
if (0 == [[AliTigerTally sharedInstance] init:appKey collectType:TT_DEFAULT options:options listener:nil]) {
NSLog(@"Initialization successful");
} else {
NSLog(@"Initialization failed");
}
// カスタムプライバシー:基本および拡張デバイスデータを除外
TTCollectType collectPrivacy = TT_NO_BASIC_DATA | TT_NO_EXTRA_DATA;
[[AliTigerTally sharedInstance] init:appKey collectType:collectPrivacy options:options listener:nil];
// 非プライバシー:すべてのプライバシー関連フィールドを除外
[[AliTigerTally sharedInstance] init:appKey collectType:TT_NOT_GRANTED options:options listener:nil];init を呼び出した後、vmpSign を呼び出す前に少なくとも 2 秒待機してください。この間隔は推奨事項であり、必須ではありません。SDK の保護効果を向上させることを目的としています。必要に応じてこの間隔を調整できますが、間隔が短いと保護の品質が低下する可能性があります。
ユーザーアカウントの設定 (任意)
setAccount を呼び出して、非識別化されたユーザー識別子をリクエストに関連付けます。これにより、より詳細な WAF 軽減ポリシーが可能になります。
/**
* ユーザーアカウントを設定します。
* @param account ユーザーを識別する非識別化された文字列。
*/
- (void)setAccount:(NSString *)account;ゲストユーザーの場合は、この呼び出しをスキップして初期化に進みます。ユーザーがログインした後、setAccount を呼び出してから再初期化します。
[[AliTigerTally sharedInstance] setAccount:@"<desensitized-user-id>"];リクエストへの署名
すべての例では、vmpSign を使用して wtoken 文字列を生成し、それを wToken リクエストヘッダーにアタッチします。
デフォルト署名
コンソールでカスタム署名オプションを選択していない場合は、デフォルト署名を使用します。
リクエストボディを vmpSign に渡します。ボディがないリクエスト (GET など) の場合は、nil または NSData としてエンコードされた空の文字列を渡します。
/**
* VMP 技術を使用してデータに署名します。
* @param input 署名するデータ (リクエストボディ、またはボディが空の場合は nil)。
* @return リクエスト認証用の wtoken 文字列。
*/
- (NSString *)vmpSign:(NSData *)input;例:
NSString *body = @"hello world";
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// wtoken を "wToken" HTTP リクエストヘッダーにアタッチしますカスタム署名
コンソールでカスタム署名オプションを選択した場合は、カスタム署名を使用します。まず vmpHash を呼び出して whash を生成し、その whash を vmpSign の入力として渡します。
POST、PUT、PATCH リクエストの場合:リクエストボディを
vmpHashに渡しますGET、DELETE リクエストの場合:完全な URL を
vmpHashに渡します
「whash」値を「ali_sign_whash」リクエストヘッダーに追加します。また、コンソールのシナリオベースのポリシー構成で、[カスタム署名フィールド] を「ali_sign_whash」に設定します。
/**
* カスタム署名のためにデータをハッシュ化します。
* @param type HTTP メソッド (TT_GET, TT_POST, TT_PUT, TT_PATCH, TT_DELETE)。
* @param input ハッシュ化するデータ (POST/PUT/PATCH の場合はリクエストボディ、GET/DELETE の場合は完全な URL)。
* @return whash 文字列。
*/
- (NSString *)vmpHash:(TTRequestType)type input:(NSData *)input;POST リクエストの例:
NSString *body = @"hello world";
// リクエストボディから whash を生成
NSString *whash = [[AliTigerTally sharedInstance]
vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]];
// whash から wtoken を生成
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
// whash を "ali_sign_whash" ヘッダーに、wtoken を "wToken" ヘッダーにアタッチ
NSLog(@"whash: %@, wtoken: %@", whash, wtoken);GET リクエストの例:
NSString *url = @"https://tigertally.aliyun.com/apptest";
// 完全な URL から whash を生成する
NSString *whash = [[AliTigerTally sharedInstance]
vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]];
// whash から wtoken を生成する
NSString *wtoken = [[AliTigerTally sharedInstance]
vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];vmpHashに渡される URL は、最終的なリクエスト URL と完全に一致する必要があります。一部のフレームワークは、中国語の文字またはクエリパラメーターを自動的にエンコードします。エンコーディングに違いがないことを確認してください。vmpHashは空の文字列を受け付けません。GET リクエストの場合、URL にはパスまたはクエリパラメーターを含める必要があります。
二次検証の処理
WAF が不審なアクティビティを検出すると、CAPTCHA チャレンジを返します。各レスポンスを確認し、必要に応じて CAPTCHA スライダーを表示します。
レスポンスの確認
すべてのリクエストの後に、レスポンスクッキーとボディを cptCheck に渡します。
/**
* 二次検証が必要かどうかを確認します。
* @param cookie レスポンスからのすべてのクッキー ("key=value;" 形式にマージ)。
* @param body 完全なレスポンスボディ。
* @return 0: パス。1: 二次検証が必要。
*/
- (int)cptCheck:(NSString *)cookie body:(NSData *)body;レスポンスヘッダーには複数のSet-Cookieフィールドが含まれる場合があります。cptCheckに渡す前に、それらをkey1=value1;key2=value2;のフォーマットにマージしてください。
例:
NSString *cookie = @"key1=value1;key2=value2;";
NSData *body = /* response body */;
int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
// recheck == 1: CAPTCHA スライダーを表示CAPTCHA スライダーの表示
cptCheck が 1 を返した場合、TTCaptcha スライダーを作成して表示します。
/**
* 二次検証用の CAPTCHA スライダーを作成します。
* @param view 親ビュー。
* @param option スライダー構成。
* @param delegate 検証結果のコールバック。
* @return TTCaptcha スライダーオブジェクト。
*/
- (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate;`TTOption` の構成:
| プロパティ | 型 | 説明 |
|---|---|---|
cancelable | BOOL | ユーザーがスライダーの外側をタップして閉じられるようにします |
customUri | NSString | カスタム検証ページへのパス (ローカル HTML ファイルまたはリモート URL) |
language | NSString | 表示言語 (例:中国語の場合は @"cn"、英語の場合は @"en") |
`TTDelegate` コールバック:
@protocol TTDelegate <NSObject>
@required
// 検証が成功したときに呼び出されます。
- (void)success:(TTCaptcha *)captcha data:(NSString *)data;
// 検証が失敗したときに呼び出されます。
- (void)failed:(TTCaptcha *)captcha code:(NSString *)code;
@end例:
TTOption *option = [[TTOption alloc] init];
option.language = @"cn";
option.cancelable = YES;
TTCaptcha *captcha = [[AliTigerTally sharedInstance]
cptCreate:[self view] option:option delegate:self];
[captcha show];
// TTDelegate コールバック内:
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"Verification passed: %@", data);
}
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"Verification failed with code: %@", code);
}初期化ステータスコード
| コード | 定数 | 説明 |
|---|---|---|
0 | TT_SUCCESS | 初期化に成功しました |
-1 | TT_NOT_INIT | init が呼び出されていません |
-2 | TT_NOT_PERMISSION | 必要な iOS 権限が付与されていません |
-3 | TT_UNKNOWN_ERROR | 不明なシステムエラー |
-4 | TT_NETWORK_ERROR | ネットワークエラー |
-5 | TT_NETWORK_ERROR_EMPTY | ネットワークエラー — レスポンスボディが空です |
-6 | TT_NETWORK_ERROR_INVALID | ネットワークエラー — レスポンスのフォーマットが無効です |
-7 | TT_PARSE_SRV_CFG_ERROR | サーバー構成の解析に失敗しました |
-8 | TT_NETWORK_RET_CODE_ERROR | ゲートウェイが失敗のレスポンスを返しました |
-9 | TT_APPKEY_EMPTY | AppKey が空です |
-10 | TT_PARAMS_ERROR | パラメーターエラー |
-11 | TT_FGKEY_ERROR | キー計算エラー |
-12 | TT_APPKEY_ERROR | SDK バージョンが AppKey バージョンと一致しません |
CAPTCHA エラーコード
| コード | 説明 |
|---|---|
1001 | 検証に失敗しました (ユーザー操作中または操作後のスライダー例外) |
1002 | システム例外 |
1003 | パラメーターエラー |
1005 | ユーザーによって検証がキャンセルされました |
8001 | スライダーの呼び出しに失敗しました |
8002 | スライダー検証データが異常です |
8003 | スライダー検証中の内部例外 |
8004 | ネットワークエラー |
トラブルシューティング
`vmpSign` または `vmpHash` がトークンではなくエラー文字列を返す
whash または wtoken に以下のいずれかの文字列が含まれている場合、エラーが発生しています。
"you must call init first"—initが署名の前に呼び出されていません。initを呼び出し、vmpSignを呼び出す前に少なくとも 2 秒待機してください。"you must input correct data"— 入力データが無効です。リクエストボディまたは URL が空でないか、不正な形式でないかを確認してください。"you must input correct type"—vmpHashに渡された HTTP メソッドタイプが正しくありません。
GET リクエスト署名における URL エンコーディングの不一致
GET リクエストの whash が拒否された場合、vmpHash に渡された URL が実際のリクエスト URL と一致していない可能性があります。一部のネットワークフレームワークは、クエリパラメーターや漢字を自動的にパーセントエンコーディングします。両方の URL をログに出力し、文字ごとに比較してください。
初期化が `TT_NETWORK_ERROR` (`-4`) で失敗する
デバイスがネットワークにアクセスでき、レポートドメインに到達可能であることを確認してください。中国本土以外でデプロイする場合は、options ディクショナリで Intl を 1 に設定します。特定のリージョンエンドポイントについては、CustomUrl と CustomHost を設定してください。
完全な例
以下の例は、初期化、リクエスト署名、二次検証の確認、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:^{
// ステップ 1:SDK の初期化
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
// [options setValue:@"1" forKey:@"Intl"]; // 中国本土以外の場合はコメントを解除します
int code = [[AliTigerTally sharedInstance] init:kAppkey collectType:TT_DEFAULT options:options listener:nil];
NSLog(@"tigertally init: %d", code);
// ステップ 2:署名する前に少なくとも 2 秒待機します
[NSThread sleepForTimeInterval:2.0];
// ステップ 3:リクエストへの署名 (カスタム署名の例)
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);
// デフォルト署名の場合 (カスタム署名オプションが選択されていない場合):
// wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// ステップ 4:リクエストを送信し、二次検証を確認します
[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(@"captcha check result: %d", check);
if (check == 0) return;
// 二次検証が必要 — メインスレッドで CAPTCHA を表示します
[[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 failed: %@", 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 success: %@", 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