Threat-Events™, In-App Threat Intelligence in Unity Apps with Native iOS

Last updated June 4, 2024 by Appdome

This guide walks you through integrating Appdome’s Threat-Events™ into your Unity applications, offering a step-by-step approach to enhancing mobile app security.

What are Threat-Events?

Appdome Threat-Events is a robust threat-intelligence framework for iOS apps. It consists of three elements: a Threat Event, the data from each Threat Event, and the Threat-Score™.

Threat-Events streamlines the process for mobile developers by enabling them to register for, listen to, and consume real-time attack and threat data, courtesy of Appdome’s suite of mobile app security, anti-fraud, and mobile anti-bot protection measures. This functionality offers mobile app developers the ability to customize business logic and user experience based on the user’s risk profile and the specific attack or threat presented but also ensures that their mobile application workflows are aware of attacks and threats. Furthermore, it facilitates the passing of threat data to other systems of record, such as app servers, mobile fraud analysis systems, SIEMs, and other data collection points.

Threat Events enable iOS applications to adapt and respond to mobile app attacks and threats in real time, ensuring the safety of users, data, and transactions.

Mobile Application Threat-Events vs. Threat-Scores

Appdome Threat Events can be used as a stand-alone implementation in Unity Apps or in combination with Threat Scores. Threat Events provide in-app notifications of each attack or threat, as well as the metadata associated with the attack. Threat Scores provide the mobile developer with a Threat Event event score and the combined (aggregated) mobile end-user risk score at the time of the notification.

Prerequisites

Before implementing Threat-Events or Threat-Scores in your Unity App, ensure that the following conditions are met:

  • Appdome account (create a free Appdome account here)
  • Mobile App (.ipa for iOS)
  • Appdome account class – Appdome DEV or higher.
  • Mobile Application Source Code.
  • Signing Credentials (e.g., signing certificates and provisioning profile) – see Signing Secure iOS apps.
  • Threat-Events and/or Threats have been enabled ( turned ON) for specific protection.
  • You are using the correct identifiers for the Threat-Events for each protection.

Note: The specific identifiers for each Threat Event and Threat Score are in the knowledge base article associated with each protection.

iOS

  1. Enable Threat-Events
    • Select the checkbox next to Threat-Events for the desired runtime protection.
    • This enables (turns ON) Threat-Events for that feature.
  2. Enable Threat Scores
    • Click the up/down arrow associated with Threat Scores to assign a specific score to each protection.
    • Assign a specific threat score between 1 and 999 to each protection.

Note: Threat-Events and Threat Scores can be used with or in place of server-based mobile anti-fraud solutions.

The figure below illustrates the locations of Threat Events and Threat Scores for various protections available on Appdome, including runtime mobile app security, anti-fraud, anti-malware, mobile antibot, and more.

Jailbreak Detection

Communicating with Native Platforms in Unity

To communicate with native iOS platforms in Unity, you typically use native plugins or platform-specific APIs. Here’s a more detailed guide:

  1. Native Plugins: Write code in Objective-C/Swift for iOS activity outside Unity. Connect it with Unity using Unity’s native plugin system. You can then call this code from Unity and receive messages back.
  2. Unity’s APIs: Unity offers built-in APIs (`UnityEngine.iOS`) to access device features directly from Unity scripts.
  3. Third-Party Packages: Explore Unity Package Manager or the Asset Store for pre-built solutions that simplify communication with iOS platforms.
  4. Asset Store Plugins: Check the Unity Asset Store for plugins that provide ready-made solutions for native platform integration.

Pick the method that suits your project’s needs and your team’s skills. Each method has its own pros and cons.

Code Snippet Required for Using Threat-Events with Unity Apps with Native iOS

Native iOS Plugin:

  1. Create a new Objective-C file named IOSThreatEventReceiver.mm.
  2. Add the following lines, which define the IOSRegisterReceiver native Objective-C function pointer that will register to a Threat Event:
    #import <Foundation/Foundation.h>
    typedef void (*ThreatEventCallback)(const char *message);
    @interface IOSThreatEventReceiver : NSObject
    @end
    @implementation IOSThreatEventReceiver
    +(void) registerReceiver : (ThreatEventCallback) threatEventCallback name: (NSString *) eventName
    {
        [[NSNotificationCenter defaultCenter] addObserverForName:eventName object:nil queue:nil usingBlock:^(NSNotification *note) {
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[note userInfo] options:NSJSONWritingPrettyPrinted error:nil];
            NSString *jsonDataStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
            threatEventCallback([jsonDataStr UTF8String]);
        }]; 
    }
    @end
    extern "C"
    {
    void IOSRegisterReceiver(ThreatEventCallback threatEventCallback, const char *eventName)
    {
        [IOSThreatEventReceiver registerReceiver:threatEventCallback name:[NSString stringWithUTF8String: eventName]];
    }
    }
    
  3. Place the IOSThreatEventReceiver.mm. in the Assets/Plugins/iOS folder.

C# Scripts:

  1. Create a new C# script named ThreatEventsimp.cs.
  2. Add the following lines, which add Appdome Threat-Events registration to the Start()method (using the native iOS callback IOSRegisterReceiver):
  3. Place this script in Assets/Scripts.
    using System.Runtime.InteropServices;
    using Newtonsoft.Json.Linq;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class ThreatEventsImp : MonoBehaviour
    {
        // delegate declaration
        delegate void ThreatEventCallback(string jsonDataStr);
    
        // delegate instance
        static private ThreatEventCallback threatEventCallback;
    
        // iOS native function pointer
        [DllImport("__Internal")]
        private static extern void IOSRegisterReceiver(ThreatEventCallback threatEventCallback, string eventName);
    
        void Start()
        {
            // initialize threat event callback - OnThreatEventCallback will be called when the threat event occurs
            threatEventCallback = new ThreatEventCallback(OnThreatEventCallback);
    
            // register threat events to the callback
            IOSRegisterReceiver(threatEventCallback, "BlockedKeyboardEvent");
            IOSRegisterReceiver(threatEventCallback, "BlockedClipboardEvent");  
            IOSRegisterReceiver(threatEventCallback, "BlockedScreenCaptureEvent"); 
            IOSRegisterReceiver(threatEventCallback, "JailbrokenDevice"); 
            IOSRegisterReceiver(threatEventCallback, "SslCertificateValidationFailed"); 
            IOSRegisterReceiver(threatEventCallback, "SslNonSslConnection"); 
            IOSRegisterReceiver(threatEventCallback, "SslServerCertificatePinningFailed"); 
            IOSRegisterReceiver(threatEventCallback, "UrlWhitelistFailed"); 
            IOSRegisterReceiver(threatEventCallback, "SslIncompatibleCipher"); 
            IOSRegisterReceiver(threatEventCallback, "SslIncompatibleVersion"); 
            IOSRegisterReceiver(threatEventCallback, "SslInvalidCertificateChain"); 
            IOSRegisterReceiver(threatEventCallback, "SslInvalidMinRSASignature");
            IOSRegisterReceiver(threatEventCallback, "SslInvalidMinECCSignature");
            IOSRegisterReceiver(threatEventCallback, "SslInvalidMinDigest");
            IOSRegisterReceiver(threatEventCallback, "AppIntegrityError");
        }
    
        [AOT.MonoPInvokeCallback(typeof(ThreatEventCallback))]
        static void OnThreatEventCallback(string jsonDataStr)
        {
            var userInfo = JObject.Parse(jsonDataStr);
    
            string blocked;
            string keyboard;
            string message;
            string timestamp;
            string deviceID;
            string deviceModel;
            string osVersion;
            string kernelInfo;
            string deviceManufacturer;
            string fusedAppToken;
            string carrierPlmn;
            string internalError;
            string threatEventDetailedMessage;
            string certificateSHA1;
            string certificateCN;
            string host;
            string incompatibleCipherId;
            string incompatibleSslVersion;
            string reason;
    
            string eventID = (string)userInfo["externalID"];
    
            switch (eventID)
            {
                case "BlockedKeyboardEvent":
                    // "true" or "false"
                    blocked = (string)userInfo["blocked"];
                    // Package of the keyboard
                    keyboard = (string)userInfo["keyboard"];
                    // message specified in the fusion set
                    message = (string)userInfo["message"];
                    // UNIX timestamp when event happened
                    timestamp = (string)userInfo["timestamp"];
                    // Unique device identifier
                    deviceID = (string)userInfo["deviceID"];
                    // Mobile device model
                    deviceModel = (string)userInfo["deviceModel"];
                    // Mobile device OS version
                    osVersion = (string)userInfo["osVersion"];
                    // Kernel information
                    kernelInfo = (string)userInfo["kernelInfo"];
                    // Mobile device manufacturer
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    // Build ID
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    // Carrier identity number (PLMN code)
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "BlockedClipboardEvent":
                    blocked = (string)userInfo["blocked"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];  
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "BlockedScreenCaptureEvent":
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "JailbrokenDevice":
                    // Opaque identifier of root detection method
                    internalError = (string)userInfo["internalError"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslCertificateValidationFailed":
                    // A detailed message describing the detection
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    // The certificate sha1 fingerprint
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    // The certificate CN (common name)
                    certificateCN = (string)userInfo["certificateCN"];
                    // The host that triggered the detection
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslNonSslConnection":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslServerCertificatePinningFailed":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    certificateCN = (string)userInfo["certificateCN"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "UrlWhitelistFailed":
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslIncompatibleCipher":
                    // The Incompatible Cipher Id
                    incompatibleCipherId = (string)userInfo["incompatibleCipherId"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslIncompatibleVersion":
                // The Incompatible SSL/TLS version
                    incompatibleSslVersion = (string)userInfo["incompatibleSslVersion"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslInvalidCertificateChain":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    certificateCN = (string)userInfo["certificateCN"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslInvalidMinRSASignature":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    certificateCN = (string)userInfo["certificateCN"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslInvalidMinECCSignature":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    certificateCN = (string)userInfo["certificateCN"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "SslInvalidMinDigest":
                    threatEventDetailedMessage = (string)userInfo["DeveventDetailedErrorMessage"];
                    certificateSHA1 = (string)userInfo["certificateSHA1"];
                    certificateCN = (string)userInfo["certificateCN"];
                    host = (string)userInfo["host"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
    
                    // Respond to mobile app attacks and threats here
    
                    break;
                case "AppIntegrityError":
                    // The detected tampered component
                    reason = (string)userInfo["reason"];
                    message = (string)userInfo["message"];
                    timestamp = (string)userInfo["timestamp"];
                    deviceID = (string)userInfo["deviceID"];
                    deviceModel = (string)userInfo["deviceModel"];
                    osVersion = (string)userInfo["osVersion"];
                    kernelInfo = (string)userInfo["kernelInfo"];
                    deviceManufacturer = (string)userInfo["deviceManufacturer"];
                    fusedAppToken = (string)userInfo["fusedAppToken"];
                    carrierPlmn = (string)userInfo["carrierPlmn"];
                    
                    // Respond to mobile app attacks and threats here
    
                    break;
                default:
                    Debug.Log("unknown event received " + eventID);
                    break;
            }
        }
    }
    

Special Considerations

  • Replace the event identifiers in your ThreatEventsImp according to the selections you made while fusing the application. To find the relevant Threat-Event context key, search for the specific feature via our knowledge base and navigate to the code sample section in the relevant article.
  • You can stack multiple listeners for various events for comprehensive threat detection and response capabilities.
  • Refer to the official documentation on Native iOS Plugin, BroadcastReceiver, and NSNotificationCenter for detailed information, advanced usage, and alternative implementation methods.

Meta-Data for Mobile Application Threat-Events and Threat-Scores

Below is the list of metadata that can be associated with each mobile application, Threat-Event, and Threat-Score in Unity Apps.

Threat-Event Context Keys
message Message displayed for the user on event
externalID The external ID of the event which can be listened via Threat Events
osVersion OS version of the current device
deviceModel Current device model
deviceManufacturer The manufacturer of the current device
fusedAppToken The task ID of the Appdome fusion of the currently running app
kernelInfo Info about the kernel: system name, node name, release, version and machine.
carrierPlmn PLMN of the device
deviceID Current device ID
reasonCode Reason code of the occured event
buildDate Appdome fusion date of the current application
devicePlatform OS name of the current device
carrierName Carrier name of the current device
updatedOSVersion Is the OS version up to date
timeZone Time zone
deviceFaceDown Is the device face down
locationLong Location longitude conditioned by location permission
locationLat Location latitude conditioned by location permission
locationState Location state conditioned by location permission
wifiSsid Wifi SSID
wifiSsidPermissionStatus Wifi SSID permission status

Some or all of the meta-data for each mobile application Threat-Event and Threat-Score can be consumed in Unity Apps at the discretion of the mobile developer and used, in combination with other mobile application data, to adapt the business logic or user experience when one or more attacks or threats are present.

Using Conditional Enforcement for Mobile Application Threat-Events and Threat-Scores

Conditional Enforcement is an extension to Appdome’s mobile application Threat-Event framework. By using conditional enforcement, developers can control when Appdome enforcement of each mobile application protection takes place or invoke backup, failsafe, and enforcement to any in-app enforcement used by the mobile developer.

Verifying Threat Events in Unity Apps

After you have implemented the required Threat-Event code in your Unity Apps, you can confirm that the Appdome protections in the Unity Apps properly recognize your Threat-Event implementation by reviewing the Certified Secure™ DevSecOps certificate for your build on Appdome.
Certificate Jailbreak Detection

In the Certified Secure DevSecOps certificate, an incorrect implementation of Threat Events in your mobile application is displayed, as shown below.
Missing Threat Event Jailbreak Detection

For information on how to retrieve the Certified Secure DevSecOps certification for your mobile application on Appdome, please visit the knowledge base article: Using Certified Secure™ Android & iOS Apps Build Certification in DevOps CI/CD

Related Articles

How Do I Learn More?

If you have any questions, please send them our way at support.appdome.com  or via the chat window on the Appdome platform.

Thank you!

Thanks for visiting Appdome! Our mission is to secure every app on the planet by making mobile app security easy. We hope we’re living up to the mission with your project.

Appdome

Want a Demo?

Threat-Events™ UX/UI Control

TomWe're here to help
We'll get back to you in 24 hours to schedule your demo.