Configuration
The minimum configuration for the AppProtection is setting up the username, password, and public key for the service. However, we recommend configuring all features that are present in the SDK.
The Username
, Password
and Signature Public Key
can be obtained in the Malwarelytics console. Note that these credentials are bound to your application Bundle ID. If you need to support multiple environments (Bundle IDs), you need different credentials for each environment.
Configuring the Service
To enable Malwarelytics in your app, you need to create a properly configured instance of the AppProtectionService
class and set the AppProtectionRaspDelegate
to obtain callbacks.
You should implement the following configurations:
AppProtectionConfig
- Configures service credentials and environment for the service.AppProtectionIdentificationConfig
- Configures user identification. This can be changed later at runtime.AppProtectionRaspConfig
- Configures which RASP features are enabled and default actions for the detections.AppProtectionEventConfig
- Configures which RASP events are emitted and sent to the back-end services.AppProtectionCustomerGroupingConfig
- Configuration of customer grouping and naming in the web application. For more details visit Customer Grouping and Naming
Based on the configuration, the AppProtectionRaspDelegate
then receives updates about various RASP events.
Sample Integration
The AppSecurity
swift class is a sample implementation over the AppProtectionService
.
import Foundation
import AppProtection
class AppSecurity: AppProtectionRaspDelegate {
private let appProtection: AppProtectionService
/// Creates AppSecurity instance
/// - Parameter userId: ID of the user
/// - Parameter deviceId: ID of the device
init(userId: String?, deviceId: String?) {
// Prepare the RASP feature configuration
let raspConfig = AppProtectionRaspConfig(
jailbreak: .exit("https://myurl.com/jalibreak-explained"), // exit on jailbroken phone
debugger: .block, // block debugger
reverseEngineeringTools: .notify, // let me know when user installed revers engineering tools
httpProxy: .notify, // notify me via delegate when http proxy is enabled
repackage:.exit([AppProtectionTrustedCert(withBase64EncodedString: "BASE_64_ENCODED_CERT")!], "https://myurl.com/repackage-explained"), // follow documentation how to obtain certificate string
screenCapture: .hide(), // will hide the app contents when screen is captured (for example shared via airplay)
vpnDetection: .notify // notify me when VPN is connected or disconnected
)
// Prepare the configuration for events
let eventConfig = AppProtectionEventConfig(
enableEventCollection: true, // enable event collection in general
enableScreenshotTakenCollection: true // /track screenshot events in the Malwarelytics console on the server
)
// Prepare user identification
let idConfig = AppProtectionIdentificationConfig(
userId: userId,
deviceId: deviceId
)
// Prepare a configuration for service
let config = AppProtectionConfig(
username: "$USERNAME",
password: "$PASSWORD",
signaturePublicKey: "$PUBKEY",
clientIdentification: idConfig,
raspConfig: raspConfig,
eventsConfig: eventConfig,
customerGroupingConfig: nil, // advanced feature, for more info see "Customer Grouping and Naming" section of the documentation
environment: .production
)
// Create the Service
self.appProtection = AppProtectionService(config: config)
// Set the delegate to obtain RASP callbacks
self.appProtection.rasp.addDelegate(self)
}
deinit {
// When our AppSecurity class is being "destroyed", we want
// to stop all features of the AppProtectionService.
// To do so, we need to release all its assets before
// the only reference that "holds it" is removed.
self.appProtection.release()
}
/// Report in-app incident
/// - Parameter incident: Incident that happened inside the app
func reportIncident(_ incident: RaspIncident) {
self.appProtection.events.log(incident.appProtectionValue)
}
/// Call when client id changes (paired/unpaired the app).
/// - Parameter deviceId: User id
/// - Parameter deviceId: Device id
func updateClientInfo(userId: String?, deviceId: String?) {
self.appProtection.clientIdentification.userId = userId
self.appProtection.clientIdentification.deviceId = deviceId
}
/// AppProtection resets the device indicator and starts registering it as a new device.
func resetServiceId() {
self.appProtection.resetInstanceId()
}
// MARK: - AppProtectionRaspDelegate
func debuggerDetected() {
// react to debugger
}
func jailbreakDetected() {
// react to jailbreak
}
func repackageDetected() {
// react to repackage
}
func httpProxyEnabled() {
// react to http proxy enabled
}
func userScreenshotDetected() {
// react to user screenshot
}
func reverseEngineeringToolsDetected() {
// react to reverse engineering tools
}
func systemPasscodeConfigurationChanged(enabled: Bool) {
// react to system passcode change
}
func systemBiometryConfigurationChanged(enabled: Bool) {
// react to biometry configuration changed
}
func screenCapturedChanged(isCaptured: Bool) {
// react to screen capturing (casting to different device)
}
func vpnChanged(active: Bool) {
// react to VPN state changes
}
}
enum RaspIncident {
case sslInvalidCert
case deviceUnpaired
case devicePaired
case deviceBlocked
case userIgnoredWeakPassphrase
case passphraseChanged
case biometryEnabled
case biometryDisabled
case authenticationFailed
case authenticationSuccess
var appProtectionValue: AppProtectionEvent {
switch self {
case .sslInvalidCert: return AppProtectionEvent.Network.sslInvalidCertificate
case .deviceUnpaired: return AppProtectionEvent.Authentication.deviceUnpaired
case .devicePaired: return AppProtectionEvent.Authentication.devicePaired
case .deviceBlocked: return AppProtectionEvent.Authentication.deviceBlocked
case .userIgnoredWeakPassphrase: return AppProtectionEvent.Authentication.userIgnoredWeakPassphrase
case .passphraseChanged: return AppProtectionEvent.Authentication.passphraseChanged
case .biometryEnabled: return AppProtectionEvent.Authentication.biometryEnabled
case .biometryDisabled: return AppProtectionEvent.Authentication.biometryDisabled
case .authenticationFailed: return AppProtectionEvent.Authentication.authenticationFailed
case .authenticationSuccess: return AppProtectionEvent.Authentication.authenticationSuccess
}
}
}
Service Lifecycle
Be aware that the creation of 2 instances of AppProtectionService
is considered to be a logical error and will result in application crash.
Before deiniting (removing all references to) the AppProtectionService
object, you need to call release()
that will stop all its functionality. Deiniting without release will result in application crash.
Customer Grouping and Naming
The SDK allows to pass custom values that are used to group data in Malwarelytics web console application.
The configuration items in AppProtectionCustomerGroupingConfig
add extra metadata that are passed into the web console.
The data allow to split data into groups and obtain different views on the data.
The data can be defined with:
let groupingConfig = AppProtectionCustomerGroupingConfig(
sourceBundleId: "String", // optional
appBundleId: "String", // optional
audienceGroupId: "String" // optional
)
Limitations for the strings are following:
- Max length of
sourceBundleId
is 255 characters - Max length of
appBundleId
is 255 characters - Max length of
audienceGroupId
is 20 characters
Main grouping of the data is achieved with sourceBundleId
. The value has to agree with the application credentials in the web console.
Extra granularity of data views is achieved with appBundleId
.
The last option audienceGroupId
is used to distinguish users from different customer systems such as “RETAIL”, “CORPORATE” and so on.