Before you configure anti-crawler rules for your apps, you must integrate the Anti-Bot SDK into your apps. This topic describes how to integrate the Anti-Bot SDK into iOS apps. In this topic, the Anti-Bot SDK is referred to as the SDK.
Background information
The SDK is used to sign requests that are sent by app clients. WAF verifies the request signatures to detect and block malicious requests.
Limitations
The SDK has two versions: Identifier for Advertising (IDFA) and non-IDFA. The following SDK files are provided for the two versions:
AliTigerTally_IDFA.framework
AliTigerTally_NOIDFA.framework
If you use the IDFA version, we recommend that you integrate AliTigerTally_IDFA.framework into your iOS apps. If you use the non-IDFA version, we recommend that you integrate AliTigerTally_NOIDFA.framework into your iOS apps.
The initialization process requires a long period of time to complete. You cannot call the vmpSign function before the initialization process is complete.
Your iOS apps must run on iOS 9.0 or later. Otherwise, you cannot integrate the SDK into the apps.
Prerequisites
The SDK for iOS apps is obtained.
To obtain the SDK for iOS apps, submit a ticket for technical support.
The SDK authentication key
appkey
is obtained.To obtain the SDK authentication key, enable the bot management module and create a scenario-specific protection template for apps. In the Configure Scenarios step, click Obtain and Copy AppKey for the App SDK Integration parameter. You can also use the SDK authentication key to send SDK initialization requests. The key must be included in the integration code.
NoteEach Alibaba Cloud account uses a unique
appkey
. The appkey can be used for all protected domain names of your WAF instance. You can use theappkey
to integrate the SDK into Android apps or iOS apps.Sample SDK authentication key:
****OpKLvM6zliu6KopyHIhmneb_****u4ekci2W8i6F9vrgpEezqAzEzj2ANrVUhvAXMwYzgY_****vc51aEQlRovkRoUhRlVsf4IzO9dZp6nN_****Wz8pk2TDLuMo4pVIQvGaxH3vrsnSQiK****
Step 1: Create a project
In this example, an iOS project in Xcode is created. The following figure shows a test project.
Step 2: Integrate the framework into the project
Drag AliTigerTally_IDFA.framework or AliTigerTally_NOIDFA.framework into the project.
IDFA version
Non-IDFA version
Step 3: Add dependency libraries to the project
Dependency library | Required for IDFA version | Required for Non-IDFA version |
libc++.tbd | Yes | Yes |
CoreTelephony.framework | Yes | Yes |
libresolv.9.tbd | Yes | Yes |
AdSupport.framework | Yes | No |
Step 4: Modify the Other Linker Flags setting
Add the -ObjC linker flag to the Other Linker Flags setting.
Step 5: Add the integration code
1. Add the header file
IDFA version:
#import <AliTigerTally_IDFA/AliTigerTally.h>
Non-IDFA version:
#import <AliTigerTally_NOIDFA/AliTigerTally.h>
If you use Swift, add the bridging header file that you created to the Objective-C Bridging Header option.
2. Configure request signatures
Specify a user ID that you want to include in requests. This way, you can configure Web Application Firewall (WAF) protection policies in a more efficient manner.
Parameter: account, which specifies a user ID. Data type: string. We recommend that you enter a masked user ID.
Returned value: No value or void is returned.
Sample code:
Initialize the SDK and perform one-time information collection.
If you want to collect terminal information again, call the
initialize
function again.Parameter: appkey, which specifies the SDK authentication key. Data type: string.
Returned value: An error code of the int type is returned. A value of 0 indicates that the initialization is successful. A negative value indicates that the initialization failed
Sample code:
Hash the request that you want to sign.
Hash the request that you want to sign. A whash string is generated and returned. If the request is a POST, PUT, or PATCH request, specify the request body. If the request is a GET or DELETE request, specify the requested URL. Add the whash string to the ali_sign_whash field in the HTTP request header.
Parameter description:
type: specifies the type of the request data. Data type: TTTypeRequest. Valid values:
GET: GET request.
POST: POST request.
PUT: PUT request.
PATCH: PATCH request.
DELETE: DELETE request.
data: specifies the data that you want to sign. Data type: byte[] array. Specify the request body or requested URL based on the value of the type parameter.
Returned value: A whash string is returned.
Sample code:
Sign a request.
Sign a request by calling the vmpSign function. A wtoken string is returned
Parameter: input, which specifies the data that you want to sign or the whash string that is returned by the vmpHash function. Data type: byte[] array.
Returned value: A wtoken string is returned.
Sample code:
/**
* Specify a user account.
*
* @param account The user account information.
*/
- (void)setAccount:(NSString *)account;
// You do not need to call the setAccount function to specify user IDs for guest users. You can directly call the initialize function to initialize the SDK. You need to call the setAccount function only for logged on users, and then call the initialize function.
[[AliTigerTally sharedInstance] setAccount:@"testAccount"];
/**
* Initialize the SDK.
*
* @param appkey The secret key.
* @return Indicates whether the initialization is successful.
*/
- (int)initialize:(NSString *)appkey;
// appkey is the authentication key that is assigned by Alibaba Cloud.
// Initialize the SDK and perform one-time terminal information collection. If you want to collect terminal information again, call the initialize function again.
NSString *appKey = @"xxxxxxxxxxxxxxxxxxxxx";
if (0 == [[AliTigerTally sharedInstance] initialize:appKey]){
NSLog(@"The initialization is successful.");
} else {
NSLog(@"The initialization failed.");
}
// Request type:
typedef NS_ENUM(NSInteger, TTTypeRequest) {
TTTypeGet=0, TTTypePost, TTTypePut, TTTypePatch, TTTypeDelete
};
/**
* Hash the request that you want to sign.
* @param type The type of the request data.
* @param input The data that you want to sign.
* @return whash
*/
- (NSString *)vmpHash:(TTTypeRequest)type input:(NSData *)input;
// GET request
NSString *url = @"https://tigertally.aliyun.com/apptest";
NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TTTypeGet data:[url dataUsingEncoding:NSUTF8StringEncoding]];
NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"whash: %@, wtoken: %@", whash, wtoken);
// POST request
NSString *body = @"hello world";
NSString *whash = [[AliTigerTally sharedInstance] vmpHash:TTTypePost data:[body dataUsingEncoding:NSUTF8StringEncoding]];
NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"whash: %@, wtoken: %@", whash, wtoken);
/**
* Sign the request.
* @param input The data that you want to sign.
* @return wtoken
*/
- (NSString *)vmpSign:(NSData *)input;
NSString *body = @"hello world";
NSString *wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"wtoken: %@", wtoken);
If the value of the input parameter of the vmpSign function is the whash string that is returned by the vmpHash function, set the HTTP header field to ali_sign_whash when you configure anti-crawler rules for iOS apps.
When you call the vmpHash function, make sure that the input URL is the URL in the GET request if you set the type parameter to GET. If the URL in the GET request is encoded into a string, specify the URL-encoded string for the input parameter of the vmpHash function.
The input parameter of the vmpHash function does not support empty bytes or empty strings. If you specify a URL for the data parameter of the vmpHash function, you must also specify the path or query parameters in the URL.
When you call the vmpSign function, set the input parameter to null or the Bytes value of an empty string if the request body is empty. To obtain the Bytes value of an empty string, run the "".getBytes("UTF-8") command.
If one of the following whash or wtoken strings is returned, an exception occurred during the initialization:
you must call init first: The initialize function is not called.
you must input correct data: The input data is invalid.
you must input correct type: The type of the input data is invalid.
3. Determine whether to perform two-factor authentication
Determine whether to perform two-factor authentication.
Determine whether to perform two-factor authentication based on the values of the cookie and body fields in the response. If multiple Set-Cookie headers exist, call the function after you merge the Set-Cookie headers in the cookie format.
Parameter description:
cookie: specifies all cookies in the response. Data type: string.
body: specifies all body in the response. Data type: string.
The returned value is of the INTEGER data type. Valid values: 0 and 1. The value 0 indicates that two-factor authentication is not required. The value 1 indicates that two-factor authentication is required.
Sample code:
Create a slider.
Determine whether to create a slider based on the result that is returned by the cptCheck function. The TTCaptcha object provides the Show and Dismiss methods to show or hide the slider window. TTOption encapsulates the parameters of the slider that you can configure. TTDelegate encapsulates the callback functions for the three states of slider CAPTCHA verification. If you want to specify a custom slider page, you must specify the address of the page in TTOption. You can specify an on-premises HTML file or a URL.
Parameter description:
view: specifies the page view. Data type: view.
option: specifies the slider configuration parameters. Data type: TTOption.
delegate: specifies the callback functions for the three states of slider CAPTCHA verification. Data type: TTDelegate.
Returned value: A slider is returned. Data type: TTCaptcha.
Sample code:
Error code: If the verification is abnormal, the slider failed to load. If the verification failed, the request failed to pass the verification.
Error code
Description
1001
The input parameter is invalid.
1002
A network exception occurred.
1003
The JavaScript callback data is abnormal.
1004
WebView failed to load the webpage.
1005
The returned data is abnormal.
1100
The slider window is closed by the user.
/**
* Determine whether to perform two-factor authentication.
*
* @param cookie response cookie
* @param body response body
* @return 0: indicates that two-factor authentication is not required. 1: indicates that two-factor authentication is required.
*/
- (int)cptCheck:(NSString *)cookie body:(NSString *)body;
NSString *cookie = @"key1=value1;kye2=value2;";
NSString *body = "....";
int recheck = [[AliTigerTally sharedInstance] cptCheck:cookie body:body];
NSLog(@"recheck: %d", recheck);
/**
* Show slider CAPTCHA verification.
*
* @param view The parent component.
* @param option The parameters.
* @param detegate The callback delegate.
*/
- (TTCaptcha *)cptCreate:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)detegate;
# pragma mark The slider callback delegate.
@protocol TTDelegate <NSObject>
@required
// The slider CAPTCHA verification is successful.
- (void)success:(TTCaptcha *)captcha data:(NSString *)data;
// The slider CAPTCHA verification failed.
- (void)failed:(TTCaptcha *)captcha code:(NSString *)code;
// A slider CAPTCHA verification exception occurred.
- (void)error:(TTCaptcha *)captcha code:(NSInteger)code message:(NSString *)message;
@end
# pragma mark The parameters.
@interface TTOption: NSObject
// Click to cancel slider CAPTCHA verification.
@property (nonatomic, assign) BOOL cancelable;
// Hide the error codes that are returned by slider CAPTCHA verification.
@property (nonatomic, assign) BOOL hideError;
// The custom page.
@property (nonatomic, strong) NSString *customUri;
// The programming language.
@property (nonatomic, strong) NSString *language;
// The trace ID of the request that is blocked by WAF.
@property (nonatomic, strong) NSString *traceId;
// The title of the slider. The title can be up to 20 characters in length.
@property (nonatomic, strong) NSString *titleText;
// The description of the slider. The description can be up to 60 characters in length.
@property (nonatomic, strong) NSString *descText;
// The color of the slider. Example: "#007FFF".
@property (nonatomic, strong) NSString *slideColor;
// Specify whether to hide the trace ID.
@property (nonatomic, assign) BOOL hideTraceId;
@end
# pragma mark The slider.
@interface TTCaptcha : NSObject
- (instancetype)init:(UIView *)view option:(TTOption *)option delegate:(id<TTDelegate>)delegate;
// Obtain the slider trace ID for data statistics.
- (NSString *)getTraceId;
// Show the slider.
- (void)show;
// Hide the slider.
- (void)dismiss;
@end
#pragma mark - TTDelegate
- (void)error:(TTCaptcha *)captcha code:(NSInteger)code message:(nonnull NSString *)message {
NSLog(@"captcha error: %ld, %@", code, message);
}
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"captcha failed: %@", code);
}
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"captcha success: %@", data);
[captcha dismiss];
}
TTOption *option = [[TTOption alloc] init];
// option.customUri = @"ali-tt-captcha-demo";
// option.traceId = @"4534534534adf433534534543";
option.titleText = @"Test Title";
option.descText = @"Test Description";
option.language = @"cn";
option.cancelable = true;
option.hideError = true;
option.slideColor = @"#007FFF";
option.hideTraceId= true;
TTCaptcha *captcha = [[AliTigerTally sharedInstance] cptCreate:[self view] option:option delegate:self];
[captcha show];
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 {
NSLog(@"captcha flow");
NSThread *thread = [[NSThread alloc] initWithBlock:^{
// Initialize the SDK.
int code = [[AliTigerTally sharedInstance] initialize:kAppkey];
NSLog(@"tigertally init: %d", code);
// Wait until the initialization of the SDK is complete.
[NSThread sleepForTimeInterval:2.0];
// Sign the request.
NSString *body = @"hello world";
NSString *whash = nil, *wtoken = nil;
// Hash and sign the request.
whash = [[AliTigerTally sharedInstance] vmpHash:TTTypePost input:[body dataUsingEncoding:NSUTF8StringEncoding]];
wtoken = [[AliTigerTally sharedInstance] vmpSign:[whash dataUsingEncoding:NSUTF8StringEncoding]];
NSLog(@"tigertally vmp: %@, %@", whash, wtoken);
// Sign the request without hashing the request.
// wtoken = [[AliTigerTally sharedInstance] vmpSign:[body dataUsingEncoding:NSUTF8StringEncoding]];
// NSLog(@"tigertally vmp: %@", wtoken);
[self doPost:kAppUrl host:kAppHost whash:whash wtoken:wtoken body:body callback:^(NSInteger code, NSString * _Nonnull cookie, NSString * _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";
// option.traceId = "4534534534adf433534534543";
option.titleText = @"Test Title";
option.descText = @"Test Description";
option.language = @"cn";
option.cancelable = true;
option.hideError = true;
option.slideColor = @"#007FFF";
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:(NSString *)body callback:(void(^)(NSInteger code, NSString* cookie, NSString *body))callback {
NSLog(@"start reqeust post");
NSURL* requestUrl = [NSURL URLWithString: url];
NSData* requestBody = [body dataUsingEncoding:NSUTF8StringEncoding];
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]);
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, body);
}];
[dataTask resume];
}
# pragma mark - TTDelegate
- (void)error:(TTCaptcha *)captcha code:(NSInteger)code message:(nonnull NSString *)message {
NSLog(@"captcha error: %ld, %@", code, message);
}
- (void)failed:(TTCaptcha *)captcha code:(nonnull NSString *)code {
NSLog(@"captcha failed: %@", code);
}
- (void)success:(TTCaptcha *)captcha data:(nonnull NSString *)data {
NSLog(@"captcha success: %@", data);
[captcha dismiss];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:data preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Confirm" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:true completion:nil];
}
@end