To configure scenario-based mitigation policies for app protection in the BOT Management console, you must integrate the Web Application Firewall (WAF) App Protection software development kit (SDK) into your application. This topic describes how to integrate the SDK for an iOS application.
Background information
The App Protection SDK is primarily used to sign requests that are initiated from app clients. The WAF server verifies the signatures of app requests to detect threats in your application services and block malicious requests. This helps protect your application.
Limits
The iOS SDK is available in two versions: an identifier for advertisers (IDFA) version and a non-IDFA version. The corresponding SDK files are:
AliTigerTally_IDFA.framework
AliTigerTally_NOIDFA.framework
You can integrate the AliTigerTally_IDFA version of the SDK if your iOS project uses IDFA. Otherwise, you can use the AliTigerTally_NOIDFA version.
The init interface call is time-consuming. To ensure full security protection, we recommend that you wait at least 2 seconds after you call the init interface before you call the vmpSign signing interface. This interval is a recommendation, not a requirement, and is intended to improve the effectiveness of the SDK's protection. You can adjust the interval based on your business needs. However, a shorter interval may prevent the security capabilities from taking full effect.
The iOS application must be running iOS version 9.0 or later. The App Protection SDK cannot be integrated with earlier versions.
Prerequisites
You have obtained the SDK for your iOS application.
To obtain the SDK, you can submit a ticket to contact a product technical expert.
NoteThe SDK file for an iOS application is named tigertally-X.Y.Z-xxxx-ios.zip, where X.Y.Z represents the version number. The file contains two framework files and two xcframework files.
You have obtained the SDK authentication key (appkey).
After you enable bot management, go to the page. In the app list, click Obtain and Copy AppKey to obtain the SDK authentication key. This key is required for SDK initialization requests and must be included in the integration code.
NoteEach Alibaba Cloud account has a unique appkey that applies to all domain names protected by WAF. This appkey is used for SDK integration in Android, iOS, and Harmony applications.
Example authentication key: ****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****.
Step 1: Create a new project
This example uses the Xcode environment. You can create a new iOS project and complete the setup wizard. The project directory is created as shown in the following figure.
Step 2: Integrate the framework package
Add the SDK file, AliTigerTally_IDFA.framework or AliTigerTally_NOIDFA.framework, to your project.
IDFA version

Non-IDFA version

Add the Captcha module SDK file, AliCaptcha.framework, to your project.

Add the resource file AliCaptcha.bundle to your project.

Step 3: Add dependency libraries
Dependency library | Required for IDFA version | Required for non-IDFA version |
libc++.tbd | Yes | Yes |
libresolv.9.tbd | Yes | Yes |
CoreTelephony.framework | Yes | Yes |
AdSupport.framework | Yes | No |
AppTrackingTransparency.framework | Yes | No |

Step 4: Edit options
In the Other Linker Flags option, add -ObjC.
Step 5: Add the integration code
1. Add the header file
For the IDFA version, use the following configuration:
#import <AliTigerTally_IDFA/AliTigerTally.h>For the non-IDFA version, use the following configuration:
#import <AliTigerTally_NOIDFA/AliTigerTally.h>
2. Set the data signature
You can set a custom end-user identity for your business. This lets you configure WAF mitigation policies with greater flexibility.
/** * Sets the user account. * * @param account The account information. */ - (void)setAccount:(NSString *)account;Parameters
account: A string that identifies a user. Use a desensitized format. Type: NSString.
Return value: None.
Example
// For guest users, you can skip setAccount and proceed with initialization. After a user logs on, call setAccount and re-initialize. [[AliTigerTally sharedInstance] setAccount:@"testAccount"];
Initialize the SDK to perform the initial data collection.
Each initialization collects device information once. You can call the init function again to re-initialize data collection for different business scenarios.
Initialization supports three data collection modes: full, custom privacy, and non-privacy. The non-privacy mode does not collect fields that involve end-user privacy, such as IDFA and IDFV.
NoteTo comply with your internal requirements, select a suitable data collection mode that ensures data integrity. Complete data helps detect potential threats more effectively.
// Initialization callback. The callback returns the status code of the interface call. typedef void (^TTInitListener)(int); /** * Initializes the SDK. * * @param appkey The key. * @param options Optional parameters. * @param onInitFinish The callback for when initialization is complete. * @return Indicates whether the initialization was successful. */ - (int)init:(NSString *)appkey collectType:(TTCollectType)type options:(NSMutableDictionary *_Nullable)options listener:(TTInitListener _Nullable)onInitFinish;Parameters
appkey: Your SDK authentication key. Type: NSString.
collectType: The data collection mode. Type: TTCollectType. Valid values:
Field
Description
Example
TT_DEFAULT
Collects full data.
TT_DEFAULT
TT_NO_BASIC_DATA
Does not collect basic device data.
This includes the device name, system version number, and screen resolution.
TT_NO_X | TT_NO_Y
(Indicates that neither X nor Y is collected. X and Y represent the field type names of specific collection items.)
TT_NO_UNIQUE_DATA
Does not collect unique identifier data.
This includes IDFV and IDFA.
TT_NO_EXTRA_DATA
Does not collect extended device data.
This includes connected Wi-Fi information (SSID, BSSID) and the list of nearby Wi-Fi networks.
TT_NOT_GRANTED
Does not collect any of the preceding privacy data.
TT_NOT_GRANTED
options: Optional parameters for data collection. The default value is nil. Type: NSMutableDictionary. The following parameters are available:
Field
Description
Example
IPv6
Specifies whether to use an IPv6 domain name to report device information.
0 (default): Uses an IPv4 domain name.
1: Uses an IPv6 domain name.
1
Intl
Specifies whether to use a domain name outside the Chinese mainland to report device information.
0 (default): Reports from the Chinese mainland.
1: Reports from outside the Chinese mainland.
1
CustomUrl
The domain name of the data reporting server.
https://cloudauth-device.us-west-1.aliyuncs.com
CustomHost
The host of the data reporting server.
cloudauth-device.us-west-1.aliyuncs.com
NoteFor most international regions, you only need to set the Intl parameter. You can set CustomUrl and CustomHost only when you need to report data to a specific region, such as the US (Silicon Valley) region: https://cloudauth-device.us-west-1.aliyuncs.com.
listener: The SDK initialization callback interface. You can use this callback to determine the status of the initialization. The default value is nil. Type: TTInitListener.
TTCode
Code
Description
TT_SUCCESS
0
The SDK is initialized.
TT_NOT_INIT
-1
The SDK initialization is not called.
TT_NOT_PERMISSION
-2
The basic iOS permissions required by the SDK are not fully granted.
TT_UNKNOWN_ERROR
-3
An unknown system error occurred.
TT_NETWORK_ERROR
-4
A network error occurred.
TT_NETWORK_ERROR_EMPTY
-5
A network error occurred. The returned content is an empty string.
TT_NETWORK_ERROR_INVALID
-6
The format of the network response is invalid.
TT_PARSE_SRV_CFG_ERROR
-7
Failed to parse the server configuration.
TT_NETWORK_RET_CODE_ERROR
-8
The gateway returned a failure response.
TT_APPKEY_EMPTY
-9
The AppKey is empty.
TT_PARAMS_ERROR
-10
Other parameter errors occurred.
TT_FGKEY_ERROR
-11
A key calculation error occurred.
TT_APPKEY_ERROR
-12
The SDK version does not match the AppKey version.
Return value: An error code. 0 indicates success. A negative number indicates failure. Type: int.
Example:
// The appkey is the authentication key assigned on the Alibaba Cloud platform. NSString *appKey = @"xxxxxxxxxxxxxxxxxxxxx"; // Optional parameters. You can configure IPv6 and international reporting. NSMutableDictionary *options = [[NSMutableDictionary alloc] init]; [options setValue:@"0" forKey:@"IPv6"]; // Configure for IPv4. [options setValue:@"0" forKey:@"Intl"]; // Configure for reporting from the Chinese mainland. // [options setValue:@"1" forKey:@"Intl"]; // Configure for reporting from outside the Chinese mainland. // Report to the US (West) site. // [options setValue:@"https://cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomUrl"]; // [options setValue:@"cloudauth-device.us-west-1.aliyuncs.com" forKey:@"CustomHost"]; // One initialization call represents one device information collection. You can call the init function again to re-initialize collection for different business scenarios. // Full data collection. if (0 == [[AliTigerTally sharedInstance] init:appkey collectType:TT_DEFAULT options:options listener:nil]) { NSLog(@"Initialization successful"); } else { NSLog(@"Initialization failed"); } // Specify privacy data to collect. You can use "|" to concatenate different privacy data types. TTCollectType collectPrivacy = TT_NO_BASIC_DATA | TT_NO_EXTRA_DATA; int ret = [[AliTigerTally sharedInstance] init:appkey collectType:collectPrivacy options:options listener:nil]; // Do not collect privacy fields. int ret = [[AliTigerTally sharedInstance] init:appkey collectType:TT_NOT_GRANTED options:options listener:nil];
Data hashing.
This is a custom signing interface that calculates the input data to generate a whash string as the custom signature data. For POST, PUT, and PATCH requests, you must pass the request body. For GET and DELETE requests, you must pass the full URL. The whash string must also be added to the ali_sign_whash field in the HTTP request header.
// Request type: typedef NS_ENUM(NSInteger, TTRequestType) { TT_GET=0, TT_POST, TT_PUT, TT_PATCH, TT_DELETE }; /** * Hashes the custom signature data. * @param type The data type. * @param input The data to be signed. * @return The whash string. */ - (NSString *)vmpHash:(TTRequestType)type input:(NSData *)input;Parameters:
type: The data type. Type: TTTypeRequest. Valid values:
GET
POST
PUT
PATCH
DELETE
input: The data to be signed. Pass the body or URL based on the type. Type: NSData.
Return value: The whash string. Type: NSString.
Example:
// GET request NSString *url = @"https://tigertally.aliyun.com/apptest"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@", whash); // POST request NSString *body = @"hello world"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@", whash);
NoteYou do not need to call this interface if you configure the default signature in the console and the custom signing option is not selected. If you select the custom signing option, call this interface for hash verification before data signing.
Data signing.
This method uses VMP technology to sign the input data and returns a wtoken string for request authentication.
/** * Signs the data. * @param input The data to be signed. * @return The wtoken string. */ - (NSString *)vmpSign:(NSData *)input;Parameters:
input: The data to be signed. This is typically the entire request body or the whash from custom signing. Type: NSData.
Return value: The wtoken string. Type: NSString.
Example:
// Default signature configured in the console (custom signing is not selected). NSString *body = @"hello world"; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"wtoken: %@", wtoken); // Custom signing configured in the console. // Custom signing for a POST request. NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_POST input:[body dataUsingEncoding:NSUTF8StringEncoding]]; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@, wtoken: %@", whash, wtoken); // Custom signing for a GET request. NSString *url = @"https://tigertally.aliyun.com/apptest"; NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TT_GET input:[url dataUsingEncoding:NSUTF8StringEncoding]]; NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]]; NSLog(@"whash: %@, wtoken: %@", whash, wtoken);NoteWhen you call vmpHash for custom signing, the input parameter for the vmpSign interface is the generated whash string. When you configure a scenario-based policy for app protection, you must set the Custom Signing Field to ali_sign_whash.
When you call vmpHash to generate a whash for a GET request, make sure that the input URL is identical to the final network request URL. Pay close attention to URL encoding because some frameworks automatically encode Chinese characters or parameters.
The input parameter of the vmpHash interface does not support bytes or empty strings. If the input is a URL, it must contain a path or a parameter.
When you call vmpSign, if the request body is empty, such as the body of a POST or GET request, pass a null object (nil) or the NSData value of an empty string. For example, [@"\"\" dataUsingEncoding:NSUTF8StringEncoding].
If whash or wtoken is one of the following strings, an exception occurred during initialization:
you must call init first: The init function was not called.
you must input correct data: The input data is incorrect.
you must input correct type: The input type is incorrect.
3. Secondary verification
Determine the result.
You can determine whether to perform secondary verification based on the cookie and body fields in the response. The header may contain multiple Set-Cookie fields. You must merge them into the cookie format before you call this interface.
/** * Checks whether to perform secondary verification. * * @param cookie The response cookie. * @param body The response body. * @return 0: Pass, 1: Secondary verification required. */ - (int)cptCheck:(NSString *)cookie body:(NSData *)body;Parameters:
cookie: All cookies in the request response. Type: NSString.
body: The entire body of the request response. Type: NSData.
Return value: The decision result. 0 indicates pass, and 1 indicates that secondary verification is required. Type: int.
Example:
NSString *cookie = @"key1=value1;key2=value2;"; NSData *body = xxx; int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body]; NSLog(@"recheck: %d", recheck);
Create a slider.
Based on the result that is returned by cptCheck, you can decide whether to create a slider object. The TTCaptcha object provides the show and dismiss methods to display and hide the slider window. TTOption encapsulates the configurable parameters for the slider. TTListener includes callbacks for two slider statuses. To customize the slider window, you must pass the address of the custom page. You can use local HTML files or remote pages.
/** * Displays the slider for verification. * * @param view The parent component. * @param option The parameters. * @param detegate The callback protocol. */ - (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)detegate; @protocol TTDelegate <NSObject> @required // Slider verification is successful. - (void)success:(TTCaptcha *)captcha data:(NSString *)data; // Slider verification failed. - (void)failed:(TTCaptcha *)captcha code:(NSString *)code; @end @interface TTOption : NSObject // Click to cancel. @property (nonatomic, assign) BOOL cancelable; // Custom page. @property (nonatomic, strong) NSString *customUri; // Language. @property (nonatomic, strong) NSString *language; @end @interface TTCaptcha : NSObject - (instancetype)init:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate; // Obtains the slider traceId for data statistics. - (NSString *)getTraceId; // Displays the slider. - (void)show; // Cancels the slider. - (void)dismiss; @endParameters:
view: The current page view. Type: View.
option: The slider configuration parameters. Type: TTOption.
listener: The slider status callback. Type: TTDelegate.
Return value: The slider object. Type: TTCaptcha.
Example:
#pragma mark - TTDelegate - (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code { NSLog(@"captcha failed: %@", code); } - (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data { NSLog(@"captcha success: %@", data); } TTOption *option = [[TTOption alloc] init]; // option.customUri = @"ali-tt-captcha-demo-ios"; option.language = @"cn"; option.cancelable = true; TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self]; [captcha show];NoteA
Verification failederror indicates that an exception was detected while or after the user moves the slider.The detailed error codes are listed below:
1001: Verification failed.
1002: System exception.
1003: Parameter error.
1005: Verification canceled.
8001: Failed to invoke the slider.
8002: The slider verification data is abnormal.
8003: Internal exception during slider verification.
8004: Network error.
Best practices example
#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];
// Do any additional setup after loading the view.
[self doTest];
}
- (void)doTest {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
// Initialize.
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
// [options setValue:@"1" forKey:@"Intl"]; // Configure for international reporting.
int code = [[AliTigerTally sharedInstance] init:kAppkey collectType:TT_DEFAULT options:options listener:nil];
NSLog(@"tigertally init: %d", code);
// Do not call synchronously right away.
[NSThread sleepForTimeInterval:2.0];
// Sign
NSString *body = @"hello world";
NSString *whash = nil, *wtoken = nil;
// Custom signing.
whash = [[AliTigerTally sharedInstance] vmpHash:TT_Post input:[body dataUsingEncoding:NSUTF8StringEncoding]];
wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"tigertally vmp: %@, %@", whash, wtoken);
// Default normal signing.
// wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// NSLog(@"tigertally vmp: %@", wtoken);
[self doPost:kAppUrl host:kAppHost whash:whash wtoken:wtoken body:[body dataUsingEncoding:NSUTF8StringEncoding] callback:^(NSInteger code, NSString * _Nonnull cookie, NSData * _Nonnull body) {
int check = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
NSLog(@"captcha result:%d", check);
if (check == 0) return;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self doShow];
}];
}];
}];
[thread start];
}
- (void)doShow {
NSLog(@"captcha show");
TTOption *option = [[TTOption alloc] init];
// option.customUri = @"ali-tt-captcha-demo-ios";
option.language = @"cn";
option.cancelable = true;
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 {
NSLog(@"start reqeust post");
NSURL* requestUrl = [NSURL URLWithString: url];
NSData* requestBody = body;
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 = requestBody;
NSURLSessionDataTask* dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"tiger tally sign failed: %@", [error description]);
callback(-1, nil, [[error description] dataUsingEncoding:NSUTF8StringEncoding]);
return;
}
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
NSInteger code = httpResponse.statusCode;
NSString* body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSMutableString* cookies = [[NSMutableString alloc] init];
for (NSHTTPCookie* cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if ([url containsString:[cookie domain]]) {
NSLog(@"domain:%@, path: %@, name:%@, value: %@", [cookie domain], [cookie path], [cookie name], [cookie value]);
[cookies appendFormat: @"%@=%@;", [cookie name], [cookie value]];
}
}
NSLog(@"reponse code: %ld", code);
NSLog(@"reponse cookie: %@", cookies);
NSLog(@"reponse body: %@", body ? (body.length > 100 ? [body substringToIndex:100]:body) : @"");
callback(code, 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:true 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:true completion:nil];
}
@end