This documentation provides a detailed reference manual for using Approov. If you are new to Approov then you might find it easier to start with the Quickstart Integrations for the frontend and backend. You can then return to this manual as needed for detailed usage guidance.
Once you have installed the Approov CLI Tool there are a few more steps to get your Approov integrated app running. These are set out in roughly the expected order over the next few sections of this manual:
After following these steps you should have a working Approov integration, although there are of course other features you can try. In particular, we strongly recommend that you configure pins and implement pinning in the app.
Other aspects you might want to try are:
HS256
signing method.If you have technical support enquiries then please use support@approov.io
. Note that if you have a paid plan then you can use the command approov support
to get emergency contact information. This will allow you to contact our 24 hour support for critical production issues.
You can reach your subscription the portal at https://approov.chargebeeportal.com. If you are a new account holder, you can request a new sign-up link at the bottom of the portal page, otherwise use your credentials to log in. The portal allows you to change your subscription plan, update your payment details, terminate your subscription, etc. For further sales-related enquiries, please contact sales@approov.io
When you initialize access to your Approov account through the CLI a number of different access roles may have been installed. Typically these will have been dev
and admin
. The dev
role is automatically made active on installation.
Most operations can be carried out using the dev
role, but certain more security sensitive operations are reserved so that they can only be executed when you have assumed an admin
role. To assume the admin
role you can type:
$ eval `approov role admin`
When you issue the next approov CLI command you may be invited to provide the password. You should only assume the admin
role for the period that you need it and then you should assume the dev
role again. Note that on Windows you must use the form set APPROOV_ROLE=admin:myaccount
where myaccount
is the name of your Approov account.
To see a list of all the roles available use:
$ approov role
available role selection commands for account myaccount:
eval `approov role admin`
eval `approov role dev`
approov role .
This lists all of the user roles you have installed on your machine, and the command required to assume that role. The command you actually need to issue is eval
which evaluates the return from the approov role
command itself. This is a string for exporting the APPROOV_ROLE
environment variable, which selects the active role. This ensures that each individual shell, which can have a different APPROOV_ROLE
values, may have a different role selected. This prevents any unwanted interaction between terminal shells when you select a different role as the selection is only for the current shell. Any newly created shell will default to using the dev
role, if that is available.
Note that on the Windows platform the role
commands look slightly different, such as the following:
$ approov role
available role selection commands for account myaccount:
set APPROOV_ROLE=admin:myaccount
set APPROOV_ROLE=dev:myaccount
approov role .
Thus you must issue these set
commands directly to change the current role.
User role sessions are controlled by the Approov cloud. This means that if you have initialized the same access on multiple machines, operations that end a session on one machine will impact all other machines using the same user roles.
If you explicitly select a role again, after you have provided a password, then this ensures that the session is refreshed to be a full hour long. You can use this if you wish to perform some scripted operations using the Approov CLI and you do not want the session to time out during this time. You can use the form approov role .
to extend the session for the currently active role without have to explicitly select it.
It is possible to see which role is selected at any time as follows:
$ approov whoami
admin: https://admin-something.approovr.io
configuration: /home/me/.approov
account: myaccount
role: dev
ID: dev-1016
name: my name
email: me@mydomain.com
expiry: 2020-12-04 14:00:30
The shows both the role
and account
, as well as other relevant information.
Note also that it is also possible to initialize access to multiple different Approov accounts, e.g.:
$ approov role
available role selection commands:
eval `approov role admin myaccount`
eval `approov role dev myaccount`
eval `approov role dev myotheraccount`
approov role .
In this case an additional parameter is always required to specify the account as well as the role required.
If the access provided expires or is revoked then the role will remain on your machine but you will no longer be able to use it. If you execute an approov init
with revised access then that will replace any expired or revoked user role. You can remove all current roles using the approov init remove-all
command, but then you will need to reinitialize access via the access recovery flow.
Password protection improves your security posture and dramatically reduces the risk of your account being compromised. Approov access information is held in the .approov
configuration in your home directory and this will only have access permissions for your user identity. However, if your machine account is compromised then so could access to your Approov account. This could allow an attacker to generate valid Approov tokens or prevent your apps from doing so. Password protection of Approov user roles prevents this. Sessions time out after a maximum of one hour and after this time the password is required again for access. The password itself is never stored by the Approov CLI on your machine.
Communication between the Approov CLI and the Approov cloud service is always conducted using TLS, but its security is subject to the certificate trust store installed on your local machine. Ordinarily if this were to be compromised then it would be possible for a Man-in-the-Middle (MITM) attacker to intercept, modify and then spoof requests to the Approov cloud service. The password mechanism also provides protection from such compromises, as it provides an additional level of message integrity over and above TLS. It employs asymmetrically encrypted integrity tokens with a proof of password possession protocol to transmit this information, ensuring that an attacker cannot spoof these integrity tokens without knowledge of the password.
If a CLI command is issued that uses a password protected role, then the user will be invited to provide the password before they can continue. Once the password is correctly entered this initiates an active session that lasts for one hour. During that time it is not necessary to enter the password again in order to issue CLI commands. The current session information is stored in a .approov
file in your home directory.
Different users should not share user roles, you should add new user roles as required, allowing each user to set their own passwords.
If you wish to reset the password for a user role then first select the role. Then use the following command:
approov password -sendCode
password reset code for dev role in account myaccount sent to me@mydomain.com
use the command "approov password -setWithCode <emailed-code>" to set the password
This sends the reset information to the email associated with the user role. The email provides a long base64url
encoded reset code string that is valid for at least 30 minutes. This long code is actually used to encrypt the new password information sent to the Approov cloud. This ensures that no MITM can possibly intercept the password information, or set their own passwords, without access to the code in the email.
You can then set the password as follows (use the code received in your email, not the one listed below):
$ approov password -setWithCode Gbf4erRmu3lrWAGAIW60kMXlkSYK1jJmtqRicMFAZCg
setting password for dev role in account myaccount
enter password:
confirm password:
new password set for dev role in account myaccount
You will be asked to enter a password (of at least six characters) and then re-enter for confirmation. It is not echoed to the terminal.
After a password has been set, you will be asked to enter it the next time you assume the role, or when the session has timed out with the role active and you issue another command.
Note it is also possible, although not recommended, to remove the need to use a password for a paricular user role. Go through the procedure of obtaining a reset code and then:
$ approov password -clearWithCode Gbf4erRmu3lrWAGAIW60kMXlkSYK1jJmtqRicMFAZCg
WARNING: we recommend that you protect your access using passwords
ATTENTION: If you wish to continue then please type YES and return: YES
password cleared for dev role in account myaccount
It may take up to 30 seconds for the new password state to be fully updated.
If you are installing the Approov CLI onto another machine, or if your Approov configuration is somehow lost, then you will need to use the recovery flow. This will send a new onboarding email to your email address with a refreshed onboarding code allowing you to run approov init
again. Note that, for security reasons, you must have a record of the recovery PIN that was provided during the original initialization. This is to prevent an attacker gaining access to your Approov account if they are able to gain access to your email account.
The recovery command is included in the original onboarding email, and will be similar to the following:
$ approov init myaccount me@mydomain.com aprv2-svpc.critical.blue
you must provide the recovery PIN that you were given when you first initialized Approov access for this account
enter PIN and return: 123456
onboarding email for account myaccount to me@mydomain.com will be sent if this combination is valid (wait 5 minutes before retrying)
You must specify the account name and the email address to which the access was issued. You will also be asked for the recovery PIN. If all this information is correct then you will receive an Approov Onboarding
email. This email will only ever be sent to the address to which the role was assigned (you can’t use this to recover access if your email address has changed). No indication is given if the provided information is incorrect, the command will just silently fail. Note that, if you think some of the information is incorrect on one attempt, you must wait 5 minutes before retrying due to the protections in place agaist the brute forcing of the PIN value.
If you signed up to use an earlier version of Approov then you will have been issued with management tokens, rather than an onboarding code. This will have been set in an APPROOV_MANAGEMENT_TOKEN
environment variable. It is possible to import your existing management tokens to use the new role based approach as follows:
$ approov init mydev.tok
imported management token dev-6805 for account myaccount
available role selection commands for account myaccount:
eval `approov role dev`
This imports the management token saved in the file mydev.tok
. You should now remove the environment variable APPROOV_MANAGEMENT_TOKEN
.
You will probably also want to import your myadmin.tok
management token as well.
Importing existing management tokens doesn’t add a password for them. However, you can do this with the approov password -sendCode
command when the appropriate role is selected. See Passwords.
This describes the overall architecture of Approov and familiarizes you with the key concepts required to understand the steps in the Approov integration.
The key components of Approov are detailed in the following diagram:
The steps required to obtain an Approov token are as follows:
approov
command line tool and Approov account access you were issued upon sign up. A key aspect of this administration is the registration of new apps that are to be released to the app store. The approov
tool analyzes the app (in either .apk
, .aab
or .ipa
format) and adds its signature to a database in the Approov cloud service for your account. The particular build of the app then becomes recognized as being official, allowing valid Approov tokens to be generated for calls from that app.fetchApproovToken
or fetchApproovTokenAndWait
methods in the Approov SDK that must be integrated into your app. Note that these calls must only be made after having initialized the Approov SDK. If this is the first call to obtain an Approov token, then it will initiate an integrity measurement process inside the SDK that requires communication with the Approov cloud service. Once an Approov token has been obtained it is cached by the SDK for up to 5 minutes so that subsequent fetch calls do not require additional network communication.Approov-Token
, but the approach will depend on your particular API. It is important that all communications made by these APIs are pinned so that no Man-in-the-Middle (MitM) interception is possible that could make a copy of the short lived token. Pinning TLS connections is good security hygiene anyway, as it prevents customer data being intercepted in the same way.Approov is able determine various properties of the app and the environment in which it is running. The individual detections are mapped to individual property flags that are determined each time an Approov token is fetched. It is possible to set security policies that determine, at a fine grain level, whether a given detection should result in a valid Approov token being issued or not. The summary below may change at any time as new threats are recognized, but we will endeavor to keep this table as up to date as we can.
Type | Description | |
Authenticity
|
Analysis of the app to ensure it is one that has been officially registered. Failure may indicate that a fake or tampered app is being used, or one where its registration has been revoked. | |
Automation
|
Detections of the app being automated in some way. For a production app this is a strong indicator of automation that may be associated with some nefarious use of the app. Basic detection is in place for Appium iOS and monkeyrunner on Android. | |
Banning
|
Mechanisms are provided to ban particular devices so that they no longer receive valid Approov tokens. | Yes |
Cloning
|
Android app cloning is detected, which allows an app to be effectively installed inside another app, typically to allow multiple accounts on the same device. Permitting apps to run this way may have significant implications for the users security and privacy. Approov detects a multitude of these cloning apps, a popular example being Parallel Space. | No | Debugging
|
Various levels of detection are used to determine if the app is running in a debug environment. | Yes |
DeviceCheck
|
Approov provides an optional integration with Apple's DeviceCheck capability. Various propeties can be extracted from the results of this and used to determine device rejection. This may also be used to provide permanent banning of specific devices. | Yes |
Emulation
|
Detection that the app is running on an Android emulator or iOS simulator. For Android, if the SDK is running on x86, then it is more likely to be an emulator, so only an internal whitelist of known real 32-bit x86 devices are allowed through. Currently all 64-bit Android x86 devices are categorized as emulators. | Yes |
Frameworks
|
Detections for various framework and modding environments. For iOS specifically Cycript and Cydia are detected. Android specifically detects the Xposed framework. The Frida framework is detected on both Android and iOS, with protections to prevent attachment to the app while it is running. | Yes |
Filtering
|
Capabilities are provided for filtering devices based on various characteristics. This may be for information only or it may be used to reject or permanently ban certain devices. | No |
Jailbreaking
|
Detection that the iOS device the app is running on has been jailbroken. | Yes |
Rooting
|
Detection that the Android device the app is running on has been rooted. This includes detection of the Magisk root manager, even when it is being actively cloaked. | Yes |
SafetyNet
|
Approov provides an optional integration with Google's SafetyNet. Various propeties can be extracted from the results of this and used to determine device rejection. | No |
Spoofing
|
Approov provides integrity guarantees for the the data transfer between the protected core of the SDK and the Approov cloud service. Any tampering is detected and causes failing Approov tokens to be issued. | Yes |
Tampering
|
Various checks are performed to ensure the runtime integrity of the app. The overall app memory layout is also monitored looking for suspicious changes or configurations indicating that the app is being monitored or attacked. | Yes |
Unpinning
|
Detection that pinned connections between the app and the backend API are being compromised. Approov can perform continuous pinning testing and there is detection of certain frameworks designed to compromise the integrity of pinning. | Yes |
The Approov SDK is able to execute security analysis rules that are supplied dynamically by the Approov cloud service as illustrated below:
You may set your security policy and Approov researchers are continually updating the set of rules and security signatures that are being detected to indicate malicious intent inside the app’s runtime environment. When the first Approov token fetch is made in the app, the latest set of security rules are transmitted from the Approov cloud to the SDK. These rules specify the data to be gathered by the SDK and the checks to perform to identify particular threat signatures. The security rules are automatically updated for running apps if they are changed on the server.
Whenever an Approov token fetch is performed, a predetermined subset of the gathered data and the results of signature analysis are transmitted securely to the Approov cloud. Data analysis is then performed before determining if the particular app instance should be issued with a valid or an invalid Approov token.
This mechanism is also used by Approov researchers to gather intelligence on specific devices that are associated with malicious behaviour. It enables a highly reactive security stance without the requirement for SDKs within apps to be updated.
To enable a highly reliable Approov service, the backend is implemented in two different cloud service providers, as illustrated below:
When an Approov token fetch is requested, the initial transmission is sent to a the primary token service hosted in the AWS cloud. This primary service has multiple frontend servers deployed across the availability zones of a region and is setup to automatically scale with the service load. The particular geographic data center utilized is allocated upon sign up. Some accounts may also have support in multiple different geographic data centers to lower latencies in different parts of the world and to offer further enhanced redundancy.
If communication cannot be established with the primary service (or errors are continually returned) then the SDK attempts to make contact with the secondary (aka failover) system. This is available on a different domain name using a different TLD (Top Level Domain) to further enhance redundancy. The secondary failover system is implemented in Google Cloud to provide complete isolation from large scale failures that may occur in AWS. The failover system only provides a subset of the full analysis capability of the primary system, but it will ensure your apps should continue to receive valid Approov tokens in the event of a catastrophic primary system failure.
The failover system only serves Approov tokens if it is enabled. It continually checks the primary system on a minute-by-minute basis and automatically enables itself if a primary failure is detected, with no need for any manual intervention in the switch over process.
This section shows you how to get the Android Approov SDK and integrate it into your project in Android Studio.
The Android Approov SDK can be downloaded using the approov
command line tool. Use the following command to download the latest SDK package:
$ approov sdk -getLibrary approov_sdk.aar
Android SDK library 2.5.0(2974) written to approov_sdk.aar
This writes the latest available SDK package to the approov_sdk.aar
file (or any path that you specify).
You will be informed if a new SDK version is available when you register an app that contains an older version. If this happens you should consider upgrading using this command, which will always provide the latest version.
In some cases you may have been directed to use a specific version of the SDK with a particular numeric identifier. You can select it as follows, 2772 in this example:
$ approov sdk -getLibrary approov_sdk.aar -libraryID 2772
Android SDK library 2.4.0(2772) written to approov_sdk.aar
The Approov SDK minimum requirement is Android 5 (API level 21). You cannot use Approov in apps that support versions older than this.
The Approov SDK can be added to an existing app project in Android Studio using the following steps (instructions are accurate for Android Studio 4.1.1 and above):
Make sure the Project View is open (if it is not, click on Project
located on the left hand side near the top).
Depending on your Project View’s Group Tabs
setting (accessible by right-clicking on Project
), the Project View’s tabs are either visible side-by-side, or grouped together in a drop-down menu
Select the Android
tab (at the top of the Project View).
Right-click on the app
folder in the Android
tab and select New
→ Module
:
Select the Import .JAR/.AAR Package
option and click on Next
.
Enter the path for your Approov SDK .aar
that you previously downloaded into the File name
field (or navigate to the .aar
and click OK
). The Subproject name
field will be populated with the .aar
name you can also choose a different sub-project name if desired.
Click Finish
. You may be asked about adding the fiels to git
. The new subproject folder should appear in the Android
tab.
Right-click on the app
folder in the Android
tab and select Open Module Settings
.
Ensure that the app
module is selected in the sidebar on the bottom left.
Click on the Dependencies
tab and click on the green +
button (labeled Add
) in the top right corner.
Select 3 Module Dependency
.
Select the Approov SDK module that you just imported, click OK
and then click OK
again to close the window.
A Gradle sync will then run.
Once you have successfully completed these steps you are ready to start making use of the Approov SDK. The SDK methods are available by importing the package:
import com.criticalblue.approovsdk.Approov;
The Approov SDK uses the OkHttp
stack for making its requests, so the following dependency must be added to your Gradle file:
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
Please also add the following code to your gradle file:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
The following app permissions need to be available in the manifest to use the Approov SDK:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
Logging output from the Approov SDK can be seen under the Approov
tag in logcat. Normally the SDK is silent except during initialization or if there is a problem with connectivity. Approov support may require this logging to answer any issue you may raise.
The initial logging on startup will be similar to the following:
2019-05-29 11:53:06.637 4997-4997/? I/Approov: test-account, com.criticalblue.demo, 2.5.0(2974), h4gubfCFzJu81j/U2BJsdg==
It provides information about the account, app, SDK and the device ID.
We suggest you target your app at an API level below Android 11 (API level 30) if possible. This is because some aspects of the Approov SDK root checking analysis rely on PackageManager
visibility that have been restricted from this target version. This analysis is only used for the purposes of scanning for rooting apps on the device, and is never used for any fingerprinting or tracking of the devices themselves. Approov does not retain any information regarding the packages installed on particular devices and, under normal circumstances, all processing of the package information is performed on the device itself.
If you do need to target API level 30 or above then some of the root checking capabilities will not be available. Thus some devices might not be marked as rooted
or root-risk
when they otherwise would be. Other analysis, including that associated with the detection of instrumentation frameworks, is unaffected.
If you wish to retain the full root analysis capability while targetting API level 30 or above then you can use the special QUERY_ALL_PACKAGES
permission in your manifest:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
However from late 2021 Google will consider this to be a sensitive permission and you will be required to declare this in a declaration form in the Play Console. You must justify this on the basis of the security importance of detecting rooting for your application, and that the feature will not be used for any form of user fingerprinting. Google already suggests that exceptions will be forthcoming for “Apps that have a verifiable core purpose involving financial transaction functionality (e.g. dedicated banking, dedicated digital wallet) may obtain broad visibility into installed apps solely for security-based purposes.”
Note that since the Approov SDK is already obfuscated with Proguard so you should not attempt to obfuscate it again (with Proguard or R8) as this will cause it to stop working. Add the following exemption to your proguard-rules.pro
:
-keep class com.criticalblue.approovsdk.** {*;}
We recommend using a physical device for testing your apps. However, you can use an Android Virtual Device (AVD) in Android Studio. Normally the Approov service will not generate a valid Approov token when running in the emulator and will indicate a rejection wih the emulator
property. The only way to obtain a valid Approov token on an emulator is to whitelist it. This requires extracting the device ID. We suggest that the approov device -add latest
is the easiest method when first trying Approov.
This section shows you how to get the iOS Approov SDK and integrate it into your project in Xcode.
The iOS Approov SDK can be downloaded using the approov
command line tool. By default the SDK is provided as a dynamic XCFramework to allow usage with both physical devices and simulators. Use the following command to download the latest SDK package:
$ approov sdk -getLibrary Approov.xcframework
iOS SDK library 2.6.0(5647) written to Approov.xcframework
This writes the SDK into the directory Approov.xcframework
(or any path that is specified). Ensure that this is empty before you perform this operation. Note that the raw iOS package is quite large so it may take some seconds to download depending upon your connection speed. Download progress is shown.
You will be informed if a new SDK version is available when you register an app that contains an older version. If this happens you should consider upgrading using this command, which will always provide the latest version.
It is also possible to obtain a bitcode version of the SDK library. We recommend that you use the native version unless you have to use bitcode for other reasons in your release process. The bitcode version can be obtained as follows:
$ approov sdk -getLibrary Approov.xcframework -bitcode
iOS-bitcode SDK library 2.6.0(5647) written to Approov.xcframework
Obtaining the bitcode version of an SDK automatically marks that SDK has being used in bitcode mode for your account. If you didn’t obtain the SDK via the Approov CLI then it is possible to mark bitcode mode using the -bitcode
option during app registration. There are bitcode mode management commands available.
In some cases you may have been directed to use a specific version of the SDK with a particular numeric identifier. You can select it as follows, 5643 in this example:
$ approov sdk -getLibrary Approov.xcframework -libraryID 5643
iOS SDK library 2.6.0(5643) written to Approov.xcframework
Obtaining the SDK as a .xcframework
is only supported from version 2.6.0.
The Approov SDK minimum requirement is iOS 10. You cannot use Approov in apps that support versions older than this.
The Approov framework can be added to an existing app Xcode project using the following steps:
In the Xcode project editor, select the target to which you want to add the Approov SDK framework.
Select the General
tab.
Select the + (plus) icon under the Embedded Binaries
section.
Select Add Other...
in the file dialog and browse to the Approov.xcframework
obtained using the approov
command line tool.
Select Open
in the file dialog.
Ensure the Copy items if needed
destination option is checked, then select Finish.
The Approov.xcframework
entry should now be present in the Embedded Binaries
and Linked Frameworks and Libraries
sections, indicating that this will be included and linked with your app.
To access the Approov framework in source code, the public header must be imported. This can be achieved by adding the following to the top of your source or header files:
import Approov
import <Approov/Approov.h>
We recommend using a physical device for testing your apps, however, the simulator architectures are provided for you to use when this is not possible. Normally, the Approov service will not generate a valid Approov token for a simulator device. The Approov service will indicate a rejection wih the ios-simulator
property. The only way to obtain a valid Approov token on a simulator is to whitelist it. This requires extracting the device ID. We suggest that the approov device -add latest
is the easiest method when first trying Approov. Please note, using the command line to install or remove applications on the simulator may produce a different device ID. This is also the case when the simulator is reset which erases all settings and applications.
Please note, if targetting a simulator device and using xcframework
SDK, Approov does not provide support for the i386
architecture. Since Xcode might try building all the simulator architectures by default this will produce an error. You can disable any unavailable architecture in your target build options, Architectures
section as follows:
It is also possible to obtain the SDK in the classic .framework
format as follows:
$ approov sdk -getLibrary Approov.framework
iOS device SDK library 2.6.0(5647) written to Approov.framework
This writes the SDK into the directory Approov.framework
(or any path that is specified). Ensure that this is empty before you perform this operation. Note that the raw iOS package is quite large (of the order of 10MB) so it may take some seconds to download depending upon your connection speed. Download progress is shown.
By default the SDK provided is only suitable for running on physical devices. You can use the -simulator
option to obtain a different SDK that only includes simulator archiectures. It is also possible to use the -bitcode
option to obtain bitcode enabled versions of the SDK for either devices or simulators.
Note that it is also possible to obtain an SDK in the legacy .zip
format by using this extension. This provides the SDK in a zipped file of Approov.framework
.
If you download a version of the SDK prior to 2.6.0 using this method then it will contain architectures for both physical devices and simulators. This is not compatible with Xcode version 12.3 or later. You must remove the simulator architectures using lipo -remove x86_64 Approov.framework/Approov -o Approov.framework/Approov
and lipo -remove i386 Approov.framework/Approov -o Approov.framework/Approov
prior to importing into Xcode.
Logging is output by the iOS SDK into the document directory for the app in a file named Approov.log
. The SDK only generates logs during initialization or if there is a connectivity issue with the SDK. You may be asked to supply this file to Approov support if you log a support request and we are unable to diagnose the issue by interrogating our servers. Note that, you may also need to look at this file to obtain the device ID.
In order to access the log file produced by Approov, the property label Application supports iTunes file sharing
has to be added to the Information property list in Xcode and the Boolean value has to be set to Yes
. If modifying the Information property list with an external editor, the equivalent property key is UIFileSharingEnabled
and the value must be set to true
. It is then possible to use iTunes, Apple Configurator or ios-deploy to add and retrieve files to the Documents folder of an application. If using iTunes to access the Documents
folder of an application, please remember to synchronise the device for the changes to take effect.
If you are compiling your app from bitcode then it is important that the SDK you are using is marked as being in bitcode mode. This happens automatically when you download the SDK with the -bitcode
option. If the SDK was not directly downloaded using the Approov CLI then it is also possible to use the -bitcode
option when registering the app.
Commands are also provided to see the list of SDKs for your account that have bitcode mode enabled:
$ approov sdk -bitcodeList
6015: 2.6.0, iOS-universal
6243: 2.6.1, iOS-universal
It is possible to manually add a specific SDK ID as being in bitcode mode as follows, in this case for the SDK ID 5777
:
$ approov sdk -bitcodeAdd 5777
bitcode mode enabled for SDK 5777
Individual SDK IDs can then be removed from bitcode mode as follows:
$ approov sdk -bitcodeRemove 5777
WARNING: removing bitcode mode will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
bitcode mode disabled for SDK 5777
The SDK configuration is an encoded string that is used to initialize the Approov SDK in your app.
In order to initialize the SDK a configuration string must be obtained using the approov
CLI as follows:
$ approov sdk -getConfigString
#myaccount#+kv3bSO3x11jB25QN7/IdP3INz7WTpg+ScXG3vyLOJU=
The provided string can then be used as the initialConfig
parameter to initialize the Approov SDK as described in SDK Initialization. The information consists of the account identifier and public key string that uniquely identifies your account.
Initialization using this method is only supported in version 2.7.0 and later SDKs. For earlier SDKs use the extended configuration.
In some cases it may be desirable to use an extended form of the SDK configuration. This results in a much longer initial configuration string that is a base64 encoded JSON Web Token (JWT). The advantage of this extended configuration is that pinning information is accessible upon startup of the app without the need for network access. Some app development frameworks require the pinning to be set at this point. More information about the pinning approach is provided in Public Key Pinning.
The extended information contains the following information:
The JWT is signed using asymmetric Elliptic Curve Cryptography (ECC). The public key is in the configuration, but the private key is held securely in the Approov cloud service. This means that only the Approov cloud service is able to generate valid configurations. They cannot be manually modified as any tampering will be detected. The public key is transmitted by the SDK to the Approov cloud service, so any attempt to modify the public key and re-sign the configuration will also be detected.
An extended SDK configuration can be obtained using the approov
CLI as follows:
$ approov sdk -getConfig initial.config
initial SDK configuration written to initial.config
In this case the configuration is written to the file initial.config
. This file must be included inside the app to provide this information at runtime. This is described in SDK Initialization.
In order to support over-the-air dynamic updates to the configuration, the Approov cloud service can send an update if API domains or their pins (or some other parameter) is changed. Updates are also signed using ECC and the signature is checked against the public key provided in the initial configuration. This prevents any tampering of the configuration in the communication channel. If the set of APIs and/or their pins are modified in the future, then the configuration becomes out of date. The app will continue to work because it will receive an updated configuration the next time it contacts the Approov service.
You may have access to multiple Approov accounts. Each account will have a different user roles in your Approov CLI. Furthermore, each account will have a different initial configuration for the SDK. This configuration will embed a different account name and also a different public key, since each account is allocated a different public/private keypair. This prevents a configuration for one Approov account being valid for another.
These different initial configurations will need to be carefully managed in your app development and build environments. Note that, the account name that is read from the configuration is always logged as part of the Approov SDK initialization, e.g. an example from Android logcat
:
2019-05-23 16:41:43.139 32592-32592/com.criticalblue.demo I/Approov: test-account, com.criticalblue.demo, 2.0.4(1033), h4gubfCFzJu81j/U2BJsdg==
The steps required to initialize the Approov SDK are covered here. Configuration must be supplied to the SDK upon initialization. It is in the form of a string that is described in SDK Configuration. Saving and retrieving the dynamic configuration is not built into the SDK so that you can persist the configuration data in a way that best fits with the other data in your app.
This section provides a detailed reference for initializing the SDK at a low level. If you are able to use one of our Quickstart Frontend Integrations then this is done automatically as part of the integration.
The initial configuration will typically consist of a fixed short string. Substitute initialConfig
for that literal string.
If you need to use the extended configuration then, due to its length, it is perhaps better stored as a resource asset in your app. The following code loads the initial configuration approov-initial.config
from the assets of the app. In this example code we load it from a file. The name and folder location of this file may be adjusted depending upon the conventions of any particular app.
// read the extended initial configuration for the Approov SDK
String initialConfig = null;
try {
InputStream stream = getAssets().open("approov-initial.config");
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
initialConfig = reader.readLine();
reader.close();
} catch (IOException e) {
// this should be fatal if the SDK cannot read an initial configuration
Log.e(TAG, "Approov initial configuration read failed: " + e.getMessage());
}
// read the extended initial configuration
var initialConfig : String? = nil;
if let initialConfigURL = Bundle.main.url(forResource: "approov-initial", withExtension: "config") {
do {
initialConfig = try String(contentsOf: initialConfigURL)
} catch {
// it should be fatal if the SDK cannot read an initial configuration
NSLog("Approov initial configuration read failed: \(error.localizedDescription)")
}
} else {
// it should be fatal if the SDK cannot read an initial configuration
NSLog("Approov initial configuration not found")
}
The next step of the initialization is responsible for loading any dynamic configuration. In our example code we load it from a local file:
// read any dynamic configuration for the SDK from local storage
String dynamicConfig = null;
try {
FileInputStream stream = getApplicationContext().openFileInput("approov-dynamic.config");
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
dynamicConfig = reader.readLine();
reader.close();
} catch (IOException e) {
// we can log this but it is not fatal as the app will receive a new update if the
// stored one is corrupted in some way
Log.i(TAG, "Approov dynamic configuration read failed: " + e.getMessage());
}
// read any dynamic configuration for the SDK from local storage
var dynamicConfig : String? = nil;
let URLs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let dynamicConfigURL = URLs[0].appendingPathComponent("approov-dynamic.config")
do {
dynamicConfig = try String(contentsOf: dynamicConfigURL)
} catch {
// log this but it is not fatal as the app will receive a new update if the
// stored one is corrupted in some way
NSLog("Approov dynamic configuration read failed: \(error.localizedDescription)")
}
This dynamic configuration approov-dynamic.config
is held in the local file storage of the app. This configuration is optional, and when an app is launched for the first time it will not be present. The idea is that this allows a dynamically updated configuration for the app to be transmitted from the Approov cloud service to the app where it will be stored locally, and be available immediately the next time the app is started with no need to fetch it again over the network. This mechanism allows various aspects of the SDK behavior to be modified in the field, but crucially this is used to transmit updated public key pins to an app as is explained Public Key Pinning.
The next step is to actually start the SDK itself:
// initialize the Approov SDK
try {
Approov.initialize(getApplicationContext(), initialConfig, dynamicConfig, null);
} catch (IllegalArgumentException e) {
// this should be fatal if the SDK cannot be initialized as all subsequent attempts
// to use the SDK will fail
Log.e(TAG, "Approov initialization failed: " + e.getMessage());
}
// initialize the Approov SDK
do {
try Approov.initialize(initialConfig!, updateConfig: dynamicConfig, comment: nil)
} catch {
// it should be fatal if the SDK cannot be initialized as all subsequent attempts
// to use the SDK will fail
NSLog("Approov initialization failed: \(error.localizedDescription)")
}
On Android, Approov initialization requires the app Context. The initial and dynamic configurations are also provided. Remember the initialConfig
must be non-null
but the dynamicConfig
may be null
. Note that the dynamicConfig
parameter is named updateConfig
in the SDK interface.
The final (comment
) string parameter is reserved for future use and should be set to null
normally, unless you are reinitializing the SDK.
If there is a problem with the initialization then an exception will be thrown and it will not be possible to execute further methods in the SDK.
There is one final step for the overall SDK initialization:
// if we didn't have a dynamic configuration (which happens after the first launch of the app) then
// we write it to local storage now
if (dynamicConfig == null)
saveApproovConfigUpdate();
// if we didn't have a dynamic configuration (which happens after the first launch of the app) then
// we fetch one and write it to local storage now
if dynamicConfig == nil {
saveApproovConfigUpdate()
}
This ensures that on the first execution of the app after installation an updated configuration is obtained from the Approov cloud service. If there is no update then the initial configuration is written as the updated configuration, indicating that it is the latest available.
This pattern implies that the first time your app is started, after installation, a network request will be made to determine if there is any updated dynamic configuration available (this will not count towards your device billing). This approach will immediately bring a fresh app install up to date with the latest configuration. This does mean, however, that there may be a small network delay at this time if there is poor network connectivity. No update attempt is made at all if there is no network connectivity.
This calls a user defined example method saveApproovConfigUpdate
to save an updated configuration that is defined below. It should be defined as a separate method as it will need to be called whenever the SDK receives a new configuration from the Approov cloud service. This is provided as a code example outside of the Approov SDK so that you can modify it to use whatever file persistence makes the most sense for your particular app’s structure.
/**
* Saves an update to the Approov configuration to local configuration of the app. This should
* be called after every Approov token fetch where isConfigChanged() is set. It saves a new
* configuration received from the Approov server to the local app storage so that it is
* available on app startup on the next launch.
*/
public void saveApproovConfigUpdate() {
String updateConfig = Approov.fetchConfig();
if (updateConfig == null)
Log.e(TAG, "Could not get dynamic Approov configuration");
else {
try {
FileOutputStream outputStream = getApplicationContext().openFileOutput("approov-dynamic.config", Context.MODE_PRIVATE);
PrintStream printStream = new PrintStream(outputStream);
printStream.print(updateConfig);
printStream.close();
} catch (IOException e) {
// we can log this but it is not fatal as the app will receive a new update if the
// stored one is corrupted in some way
Log.e(TAG, "Cannot write Approov dynamic configuration: " + e.getMessage());
return;
}
Log.i(TAG, "Wrote dynamic Approov configuration");
}
}
/**
* Saves an update to the Approov configuration to local configuration of the app. This should
* be called after every Approov token fetch where isConfigChanged is set. It saves a new
* configuration received from the Approov server to the local app storage so that it is
* available on app startup on the next launch.
*/
func saveApproovConfigUpdate() {
let updateConfig = Approov.fetchConfig()
if (updateConfig == nil) {
NSLog("Could not get dynamic Approov configuration to save")
} else {
let URLs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let updateConfigURL = URLs[0].appendingPathComponent("approov-dynamic.config")
do {
try updateConfig!.write(to: updateConfigURL, atomically: true, encoding: String.Encoding.utf8)
} catch {
NSLog("Approov dynamic configuration write failed: \(error.localizedDescription)")
}
}
}
This calls the new fetchConfig()
method and obtains an updated configuration and writes it to local storage as approov-dynamic.config
. This will then be read the next time the app is started without having to do a network fetch.
Under normal circumstances it is only permissible to initialize the SDK once, and any attempt to initialize it a second time will result in an error.
There is one particular use case for Approov where a single app might be associated with several different Approov accounts, that might be selected as the app runs. Note though that the Approov SDK can only be associated with one account at a time.
Reinitialization is explicitly marked by providing reinit
to the comment
parameter of the initialization call.
// reinitialize the Approov SDK
try {
Approov.initialize(getApplicationContext(), initialConfig, dynamicConfig, "reinit");
} catch (IllegalArgumentException e) {
Log.e(TAG, "Approov reinitialization failed: " + e.getMessage());
}
// reinitialize the Approov SDK
do {
try Approov.initialize(initialConfig!, updateConfig: dynamicConfig, comment: "reinit")
} catch {
NSLog("Approov reinitialization failed: \(error.localizedDescription)")
}
Reinitialization is only supported in version 2.4.0 and later SDKs.
The SDK should not be reinitialized too frequently. The typical use case is that the reinitialization should only occur if the user of the app switches their account in some way, requiring access to a different set of backend APIs. Reinitialization should never be performed if there are in-flight asynchronous Approov token fetches.
If an app has access to multiple initial SDK configurations then care must be taken to also store separate dynamic SDK configuration updates for the different configurations.
This provides an overview of the management of API domains in the account. These correspond to the domains for which Approov tokens can be fetched.
Each Approov account has a set of API domains for which an app can fetch Approov tokens. These will typically correspond to the domains serving the APIs that are to be protected with Approov. An account may have a maximum of 25 different domains. API domains may be added and removed from the account as required.
In the app code, the fetchApproovToken
or fetchApproovTokenAndWait
calls are used to obtain a token. These calls have a required parameter specifying the target domain to be accessed, and a custom token for the specified domain is what’s provided by the SDK. Under the hood, each Approov attestation retrieves tokens for all registered domains and these tokens are cached securely until replacement tokens are retrieved by a new attestation.
There are a couple of motivations for specifying Approov token fetches with particular domains:
Domains are restricted to lower and upper case letters, digits, a dash and a period. This means that the API domain should not (and cannot) contain the full URI path to a particular resource.
There is no special handling of sub-domains so, for instance, mydomain.com
and sub.mydomain.com
are considered to be entirely distinct. Approov tokens should not be fetched for mydomain.com
that are then sent to sub.mydomain.com
. If required you can define a star.mydomain.com domain
and fetch Approov tokens for sub domains from that. However, if using Approov dynamic pinning, you may have to define some special logic for the Public Key Pinning Implementation to convert the domain name into one that works for the pin.
A new API domain can be added to the account as follows:
$ approov api -add mydomain.com
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain mydomain.com with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
Confirmation is required whenever a new domain is added. This is because it has an immediate impact in production, with tokens being served for the new domain. As soon as a domain is added, tokens for the API domain can be obtained from fetchApproovToken
or fetchApproovTokenAndWait
calls, using the new domain as a parameter.
You should ensure that a pinning implementation is present in your app to actually pin the connection as explained in Public Key Pinning Implementation. New domains have a public key pin added derived from the public key of the leaf certificate presented on the domain. This pin can be used to protect the communication channel between the app and API domain to prevent any Man-in-the-Middle (MITM) attack between the app and the API backend. This is explained in detail in Public Key Pinning and is not covered further here. In some cases using a leaf pin might not be appropriate, so you should consult the team responsible for maintaining the certificates for the backend API. If the API domain that you wish to connect to is not on the standard port 443, you can use the -port
option to select a different port from which to obtain the pinning information. Do not try and add the port to the API domain itself as this is not valid.
By default, newly added domains are included for continuous API monitoring. This means that you will receive an email notification if the API domain becomes inaccessible or the certificates it presents no longer match the pins in the account. Note though that you must setup the email recipients to receive these notifications.
By default, newly added domains will support attestations from mobile apps. Add the -allowWeb
flag if you also need to provide access to the API from web apps using one or more of the Web Protection Integration.
By default, Approov tokens for a particular API will be issued as signed JWS tokens using the account secret key. Approov also provides an option to issue encrypted JWE tokens for a domain. This is enabled by using the -jwe
option when adding the new API domain. The Approov token format is covered in more detail here.
Once you have added an API domain you can edit its attributes by adding it again with the new set of attribute options specified. However, editing an API domain role requires the admin role.
As explained, adding a domain in this way will set a pin based on the leaf certificate presented from the domain. This may then be used by your app to pin the connections that it makes to that domain. Pinning connections in this way significantly increases security, however, there is a danger that you app will stop communicating with the API if the certificates on the domain change and the public key of the certificate is not preserved. To avoid this, you should try to manage your backend APIs so that the same public key is used when certificates are rotated. However, if this is not possible, Approov does give you the flexibility of dynamically updating pins if required and, by default, you will receive an email from the API monitoring that will warn you of a pin mismatch.
If you have added one or more keyset keys then you can add an API domain that uses one of those keys as follows:
$ approov api -add mydomain.com -keySetKID mykey
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain twitter.com with type:keyset, alg:RS256, kid:mykey, pin:kJ8+U7wNPfc5vxwnQYvlffbaZA6wwCvd/eC8YMpB+sU=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
This adds the domain using the keyset key mykey
. This means that all Approov tokens issued for the domain will be signed or encrypted with the specified key. The key identifier will appear in the kid
claim in the header. The particular algorithm to be used is also defined by the key, allowing a wide range of choices including asymmetric options which can ensure that the Approov cloud service is the only entity capable of generating valid tokens signed with the private key, which the tokens can be verified by your backend using the public key.
An option is provided to add an API domain which only uses the Approov pinning facilities and does not need Approov tokens to be sent. Do this as follows:
$ approov api -add myotherdomain.com -pinOnly
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain myotherdomain.com with type:pinOnly, pin:kJ8+U7wNPfc5vxwnQYvlffbaZA6wwCvd/eC8YMpB+sU=
enabled continuous monitoring for https://myotherdomain.com:443 (remove using `approov monitoring -removeAPI`)
The purpose of this is to allow access to the pinning features of Approov without the need to send Approov tokens. This option should therefore only be used on endpoints where it is not necessary or possible to add Approov token checking on the backend.
Any attempt to get a token for the domain results in an UNPROTECTED_URL / unprotectedURL
status from the Approov token fetch call. The Quickstart Frontend Integrations continue with the request in this case without adding an Approov token.
A list of all the domains that are configured for the account can be obtained with:
$ approov api -list
2 API domains:
mydomain.com type:account, alg:HS256
shapes.approov.io type:restricted, alg:HS256
This provides information about all of the domains that have been added and their attributes. The type
attribute may be:
account
: This is the default, indicating that the account secret key is used.restricted
: Indicates that the keys and pins for the domain are centrally managed by Approov.keyset
: Indicates that the key used for the domain is one from the keyset.pinOnly
: Indicates that the domain has been added to enable pin management and that no tokens will be issued for it by the Approov service.The alg
attribute shows the type of algorithm used for signing or encrypting the Approov tokens. By default, for account
type tokens, the HS256
signing algorithm is used. However, if the -jwe
option was used then the A256GCMKW
JWE
encryption algorithm is used instead. For a keyset
type a wider range of algorithms are avaialble.
The kid
attribute, and the associated value, is shown if the domain has been configured to use a JWT KeySet entry. The specified key ID will be included in the header of all subsequent tokens issued for the domain.
The monitorPort
property is included if contiuous monitoring has been enabled for the domain (the default). This displays the port being used to probe the domain, normally 443. (See API Monitoring)
Approov tokens are only served, for an account, for those APIs listed by this command. Any attempt to get a token for another domain results in an UNKNOWN_URL / unknownURL
error from the Approov token fetch call (unless wildcard mode is enabled).
An API domain that has been previously added can be removed with the following command:
$ approov api -remove mydomain.com
WARNING: removing the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
removed API domain mydomain.com
Note that removal of an API domain requires an admin role and further confirmation is required before the domain is removed. Note that this means that Approov tokens will no longer be served for that domain. You obviously must be careful not to remove a domain that is being used by production apps as this will lead to a production outage.
A facility is provided to enable wildcard Approov tokens. Normally if a token fetch is made for a domain for which an API domain has not been added, then an UNKNOWN_URL / unknownURL
error is returned. In typical Approov SDK usage this will cause the API request to continue without adding an Approov token. If wildcard tokens are enabled then an Approov token will be returned always. However, if the URL is not for an explicitly added API domain then the provided Approov token will always be a failing one. This is because these domains will not be pinned and so valid Approov tokens should not be transmitted on them.
The purpose of this mode is to enable quick discovery of which particular API calls are getting Approov tokens added and to manually check if Approov tokens are being received at the backend, prior to implementing full backend Approov token checking. This initial discovery also avoids the need to setup any pinning. A gradual transition to full Approov protection can then be made, by adding API domains and turning off wildcard mode without any need to make code changes to the app.
Wildcard token mode can be enabled as follows:
$ approov api -setWildcardMode on
wildcard mode was set successfully
It may be subsequently disabled using an off
parameter value.
The current wildcard status can be seen with:
$ approov api -getWildcardMode
wildcard mode is on
A special shapes domain (managed by the Approov team) can be added to your account:
$ approov api -add shapes.approov.io
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain shapes.approov.io with type:restricted, alg:HS256
This domain serves a very simple API that provides random shapes and it is used by the Approov demo app. Any account can add this domain to generate Approov tokens for the API. The type is shown as restricted
meaning that it has the special property that the associated Approov tokens with a secret key expected by that API rather than the standard one for the account. Moreover, you are not able to modify the pins associated with that domain.
This section describes the methods that should be used to obtain an Approov token from the SDK. This is the token that is required to pass to a subsequent backend API call.
This section provides a detailed reference for fetching Approov tokens at a low level. If you able to use one of our Quickstart Frontend Integrations then this is done for you as part of the integration, typically as an interceptor. The quickstart integrations also implement pinning.
You should never cache an Approov token in your app code. Always make a call to fetch a token immediately prior to making an API request that needs it. The Approov SDK automatically caches the Approov token and only performs a network request if a new one is required. Moreover, the SDK performs additional fast checks on app integrity every time an Approov token is fetched.
Once your app is able to fetch Approov tokens, we strongly recommend that you also implement Pinning. As well as protecting your users’ data, this will also protect Approov tokens from being stolen with a Man-in-the-Middle attack.
Whenever an Approov token fetch is performed there are a set of different success or error results that may occur. These provide more information about the reason for any token fetch failure, either for debug analysis or to determine how the app should react.
Until you have Added the API Domain that you are fetching an Approov token for, as provided in the URL parameter to the fetch, you will receive an UNKNOWN_URL / unknownURL
error (unless wildcard mode is enabled).
Even if you successfully fetch an Approov token it may not be valid. When first using Approov it is a common mistake to forget to Register Your App or remember that, unless you whitelist your device, the token will also be invalid due to Approov’s debug detection if you are launched your app through the development environment.
These potential errors are enumerated in the table below along with the recommended action and a description of the associated meaning. Note that all iOS errors are prefixed by ApproovTokenFetchStatus.
.
Error (Android / iOS) | Rcmd | Description |
SUCCESS / success
|
Continue | Indicates that an Approov token was successfully retrieved. |
NO_APPROOV_SERVICE / noApproovService
|
Continue | Indicates that no token could be obtained in the unlikely event that Approov cloud services are completely down (including the failover service) or, more likely, that the account has been cancelled. The error might also occur if an app is using an old version of the Approov SDK that has no active registrations and has been deprecated and removed from the Approov cloud systems In this case the error should be propagated to the actual API call to allow Approov token checking to be disabled on the API backend. |
NO_NETWORK / noNetwork
|
Retry | Indicates no token was received because there is no network connectivity. The SDK checks for network connectivity before making a token fetch attempt. This allows a rapid return from the token fetch if there is no connectivity. If this error is received then there is no point in proceeding with any following API call which will also require network access. A timed, or user initiated, retry is required. |
POOR_NETWORK / poorNetwork
|
Retry | Indicates that no token could be obtained due to poor network connectivity. If this error is received then there is no point in proceeding with any following API call which will also require network access. A timed, or user initiated, retry is required. |
MITM_DETECTED / mitmDetected
|
Retry | Indicates that there is a Man-In-The-Middle (MITM) in the communication with the Approov cloud service. This may be malicious or may simply indicate that the end user is using a network with a firewall that intercepts all traffic for inspection. If this error is received then there is no point in proceeding with any following API call which will also require network access. A timed, or user initiated, retry is required. |
BAD_URL / badURL
|
Error | Indicates that the provided URL is not in the correct format. The URL should just be provided as a domain, although a full URL path may be specified from which the domain is extracted. This error can occur if such a URL is provided with a http:// rather than the https:// scheme.
|
UNKNOWN_URL / unknownURL
|
Error | Indicates that the provided URL is not an API domain configured for use, and wildcard mode has not been enabled. |
UNPROTECTED_URL / unprotectedURL
|
Continue | Indicates that the provided URL is valid but is not one that needs to be provided with an Approov token. The request should continue without adding an Approov token. |
NO_NETWORK_PERMISSION / NA
|
Error | For Android, indicates that the app does not have ACCESS_NETWORK_STATE or INTERNET permission.
|
MISSING_LIB_DEPENDENCY / NA
|
Error | For Android, indicates that the Approov SDK dependency on the OkHttp library has not been satisfied.
|
INTERNAL_ERROR / NA
|
Error | For Android, indicates there has been an internal error within the SDK. Please contact Approov support if this ever happens. |
NA / notInitialized
|
Error | For iOS, indicates that an attempt is being made to use an SDK method before it has been initialized. Note that the Android SDK generates an exception in this case. |
The Rcmd column shows our recommendation for how the app logic should proceed if it receives the given error state. There are three possible options:
Note that if an API domain, in active usage, is accidentally removed from your Approov account then this will lead to an error indication as the token fetch attempt will receive an Unknown URL error.
Note in cases where you are using an interceptor pattern to add your Approov token you should treat the Unprotected URL case correctly. You get this on domains that have been added with the -pinOnly
option. Thus you should continue with the request without adding an Approov token.
This describes how to fetch an Approov token with the synchronous form of the call. This method does not return until a token has been fetched. If a cached token may be used, then the call will return promptly. If a new token is required, then the call will include the time to complete the network requests with the Approov cloud service and so there may be some delay before returning. The exact delay will depend on many factors, especially network connectivity quality. This should not be called from a UI thread since the OS might force stop your application.
You must never make this call directly from the main or UI thread of your application. This is a potentially long running blocking call, and there may be unpredictable delays and effects on your app if it blocks UI processing.
The code to make the call is very simple, as follows:
Approov.TokenFetchResult approovResult = Approov.fetchApproovTokenAndWait("api.myservice.io");
if (approovResult.isConfigChanged())
saveApproovConfigUpdate();
let approovResult = Approov.fetchTokenAndWait("api.myservice.io")
if approovResult.isConfigChanged {
saveApproovConfigUpdate()
}
The method takes a parameter of the particular API domain for which a token is being fetched. This must be one that has been configured to provide Approov tokens or else an error will result.
Whenever a token fetch is performed, the recommended pattern is to check whether any new configuration has been transmitted to the app and, if so, use the saveApproovConfigUpdate
method to save it locally. This means that this configuration will be available immediately on the next app startup, even if there is no network connectivity at the time. The availability of the new configuration is checked using the isConfigChanged()
getter in the returned fetch result. Note that this flag remains set until a fetchConfig()
method, as used in saveApproovConfigUpdate
, is called. The method saveApproovConfigUpdate
is defined here. If it is not convenient or appropriate to update the configuration at that point (perhaps if the token fetching is in an interceptor) then a flag can be set to cause the update to occur at some later, more appropriate point in the code flow.
Next, check the status returned from the fetch, and react appropriately:
String token = approovResult.getToken();
if ((approovResult.getStatus() == Approov.TokenFetchStatus.SUCCESS) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.NO_APPROOV_SERVICE)) {
<pass handling>
} else if ((approovResult.getStatus() == Approov.TokenFetchStatus.NO_NETWORK) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.POOR_NETWORK) ||
(approovResult.getStatus() == Approov.TokenFetchStatus.MITM_DETECTED)) (
<retry handling>
} else {
<error handling>
}
let token = approovResult.token
if (approovResult.status == ApproovTokenFetchStatus.success) ||
(approovResult.status == ApproovTokenFetchStatus.noApproovService) {
<pass handling>
} else if (approovResult.status == ApproovTokenFetchStatus.noNetwork) ||
(approovResult.status == ApproovTokenFetchStatus.poorNetwork) ||
(approovResult.status == ApproovTokenFetchStatus.mitmDetected) {
<retry handling>
} else {
<error handling>
}
These represent the three classes of response, as discussed in the previous section. The exact implementation will depend on the structure of your app:
Note that the token fetching process itself may make various retries of the communication with the Approov server. Within the SDK, the absolute worst case timeout before the method returns is typically configured to be 30 seconds.
An alternative Approov token fetch calling mechanism is provided. This uses an asynchronous approach whereby a callback method is provided. This is called either when an Approov token is available or there is an error. This method allows token fetches to be initiated from threads that cannot be blocked, such as UI threads.
class ApproovCallbackHandler implements Approov.TokenFetchCallback {
@Override
public void approovCallback(Approov.TokenFetchResult pResult) {
if (pResult.isConfigChanged())
saveApproovConfigUpdate();
switch (pResult.getStatus()) {
case Approov.TokenFetchStatus.SUCCESS:
case Approov.TokenFetchStatus.NO_APPROOV_SERVICE:
// Go ahead and make the API call with the 'pResult.getToken()'
case Approov.TokenFetchStatus.NO_NETWORK:
case Approov.TokenFetchStatus.POOR_NETWORK:
case Approov.TokenFetchStatus.MITM_DETECTED:
// We should retry doing a fetch after a user driven event
default:
// There has been some error event that should be reported
}
}
}
// omitted code for brevity
ApproovCallbackHandler approovCallback = new ApproovCallbackHandler();
Approov.fetchApproovToken(approovCallback, "api.myservice.io");
Approov.fetchToken({ (approovResult: ApproovTokenFetchResult) in
if approovResult.isConfigChanged {
saveApproovConfigUpdate()
}
switch approovResult.status {
case ApproovTokenFetchStatus.success,
ApproovTokenFetchStatus.noApproovService:
// Go ahead and make the API call with the 'approovResult.token'
<pass handling>
case ApproovTokenFetchStatus.noNetwork,
ApproovTokenFetchStatus.poorNetwork,
ApproovTokenFetchStatus.mitmDetected:
// We should retry doing a fetch after a user driven event
<retry handling>
default:
// There has been some error event that should be reported
<error handling>
}
}, "api.myservice.io")
For Android, a class must be defined that implements the Approov.TokenFetchCallback
interface. For iOS, a closure may be defined within the call. All other handling should follow the same logic as described in the preceding sections.
The integrity check operation provided by Approov requires the SDK to perform a CPU computation and possibly one or more network requests. A Cached Fetch
, as its name implies, returns an already available token and does not require much additional computation or any network connection. The table below provides an average of values obtained by measuring the performance of an Approov token fetch operation on a range of devices for both Android and iOS with good network conditions. Moreover, the devices were located in Europe or the US West Coast, geographically close to our AWS points of presence for the Approov cloud. The type of token fetch mechanism (synchronous or asynchronous) does not impact the latency.
Platform | Network Fetch | Cached Fetch |
Android | 950 ms | 50 ms |
iOS | 750 ms | 35 ms |
The iOS Approov SDK can optionally perform additional device checks using the Apple DeviceCheck capability. If this option is used then the Approov SDK must make a call to generate a token to identify the device. This needs to be done on the first Approov token fetch after installing the app. Performing this operation requires additional network connection(s) and CPU processing time, over which the Approov SDK has no control. This can substantially delay the fetch operation on the iOS platform by adding up to 2500 ms of additional latency. If you are using this option, consider asynchronously prefetching an Approov token as early as possible to help hide this latency.
The Android Approov SDK can optionally perform SafetyNet attestation. If this option is used then the Approov SDK must make an attestation call. This needs to be done on the first Approov token fetch after installing the app. Performing this operation requires additional network connection(s) and CPU processing time, over which the Approov SDK has no control. This can substantially delay the fetch operation on the iOS platform by adding up to 3500 ms of additional latency. If you are using this option, consider asynchronously prefetching an Approov token as early as possible to help hide this latency.
If you are using Android and you have added app signing certificates, this will cause an increase to the latency of the first Approov token fetch after launching the app by an amount determined by the size of the APK and the performance of the device on which the app is running. This can add up to 1300ms for a 30MB APK on an entry-level device, but is 500ms or less for mid and high performance devices.
App registration management determines the set of apps that the Approov service will consider valid for the account.
This section is only relevant if you are using Android and are registering an app that is going to be released using Google Play Signing. This means that the app package delivered to your users will be signed by the certificate held in the Google cloud. Releases made via an Android Application Bundle are always signed in this way, but it is also possible to release APKs that are Google Play Signed, as explained in this guide.
In order to use Google Play signing with Approov you need to inform Approov about the app signing certificates that you use. These are remembered in your Approov account so it is only if you add a new certificate or revoke an old one that you will need to complete this step again. An app signing certificate proves that a particular installed app package has been issued by Google via your account. The public key of the certificate allows an encrypted digest of your full app content to be verified. Recreating a valid signature for a modified app requires access to the private key associated with the certificate, which is inaccessible except to Google. Approov supports the signature algorithms in the list of signature algorithm IDs on the Android Open Source Project’s web-site.
The app signing certificate is available in your Google Play console. Log in and navigate to the release management section for your app. You will see a screen similar to the following:
The section App signing certificate shows the signing certificate that is currently active, and its hash in various forms. Download the .der
certificate file by clicking on the DOWNLOAD CERTIFICATE button. Save it as app-signing-cert.der
, or some other convenient name. Note that there is no risk of key compromise by doing this since it is a public certificate and the private key is not included.
You can add the signing certificate to Approov with the following command:
$ approov appsigncert -add app-signing-cert.der
successfully added C0:F3:15:9B:A5:6C:A8:E1 | Subject: CN=ANOther,O=CriticalBlue,L=Edinburgh,C=UK | Expiry: 21 May 2041
This adds the certificate to those that are known by Approov. The first 8 bytes of the SHA256 hash of the certificate are shown in hex. This matches the value SHA-256 certificate fingerprint shown in the Google Play Console. Other certificate metadata and the expiry date are also output.
Approov will now be able to recognize apps that have been signed with this certificate. The Approov SDK does an analysis of the app signature as part of its analysis to ensure that only your app is able to obtain a valid Approov token. Note that in addition to providing the correct signing certificate, you also need to register the specific app version.
You may add up to 10 different app signing certificates to the Approov account. This allows apps with different signing certificates. These may be from different Google Play accounts, allowing them to be supported in a single Approov account. Moreover, if you upgrade or change your app signing certificate in the Google Play console you should keep the old one in the Approov account as there will be installed versions of your app still using it.
You can list the current app signing certificates as follows:
$ approov appsigncert -list
1 app signing certificate:
C0:F3:15:9B:A5:6C:A8:E1 | Subject: CN=ANOther,O=CriticalBlue,L=Edinburgh,C=UK | Expiry: 21 May 2041
An app signing certificate can be removed with:
$ approov appsigncert -remove C0:F3:15:9B:A5:6C:A8:E1
WARNING: removing an app signing certificate will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
successfully removed app signing certificate with fingerprint C0:F3:15:9B:A5:6C:A8:E1
Use the shortened fingerprint hash to select the appropriate certificate. Note that removal requires an admin role and user confirmation.
Adding or removing an app signing certificate does not have an immediate impact on the validity of registration. The impact is only for apps which are started 30 seconds or more after the change. Contrast this with the app registrations themselves which are actioned within 30 seconds for the next time an app fetches an Approov token (typically a maximum of 5 minutes), with no need to restart the app.
App signing certificates are also employed if you utilize the SafetyNet Integration option to check the certificate hash provided in the SafetyNet token.
Using app signing certificates will cause an increase to the latency of the first Approov token fetch after launching the app. For details please see Token Fetch Latency.
In order to obtain an .ipa
file for iOS select the Archive
option in Xcodes Product
menu (if the Archive
option is greyed out, you need to modify your target by setting the destination to be Any iOS device
since targetting a simulator device disables archiving). Once the archive operation finishes, you will see the Organizer
window similar to the one below:
Xcode has archived the source files and linked the libraries by producing an xcarchive
file which is a collection of files, binaries, debug symbols and other files from which an actual ipa
file can be obtained. From there you should click Distribute App
and select one of the options, App Store Connect
to submit to the app store or one of the others. Irrespective of the selection, you can always recall the Organizer
by selecting the menu Windows
and then Organizer
and if you select the same archive you can generate another ipa
file or make another submission to the App Store.
The Approov service is designed to only send a valid Approov token to apps that have been registered. Registration indicates that Approov has captured the fingerprint of a good instance of your app and should, subject to various other checks and conditions, deliver valid Approov tokens to any identical app instance. Any other app cannot receive valid Approov tokens and will therefore be unable to access the backend API services that you have protected with Approov.
When you release a new version of your app to the app store, you will need to register the final version that will be published. Multiple different versions of the app registration may be live simultaneously to support end users who are running different versions of the app. In general it may take some time for users’ apps to be updated to the latest version. You may also wish to register various versions of the app during the development and test process.
In order to register an app you need the app package that is to be run. This is either an .apk
or .aab
file (Android) or an .ipa
file (iOS). An app being registered must have the Approov SDK integrated within it or else the registration will fail.
$ approov registration -add my_app.apk
registering app
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=my.app.com-2.0[3]-1042 SDK:Android(2.5.0)
registration successful
$ approov registration -add my_app.ipa
registering app
DRJNqt37tsdHwb9FKFT80dPqXlOHqZnZy68zAiVSsvM=my.app.com-2.0[3]-1041 SDK:iOS(2.5.0)
registration successful
The message indicates that an overall signature of the app, its name and its version have been extracted and are available for viewing in the list of all registrations for the account. The overall signature consists of a signature hash of aspects of the application, the application package name, application version information, and the ID of the Approov SDK that is integrated within it. Finally, information about the type and version number of the Approov SDK used is also provided.
Once an app is registered, it should be possible to get valid Approov tokens for it, assuming there are no other issues with the device or its runtime environment (for instance, a debugger being active) that prevent it from receiving a valid token.
By default, an app registration is permanent and will remain in the Approov cloud database until it is explicitly removed. Permanent app registrations should be used to identify apps that are being published to production.
If you are registering an iOS IPA then you may also use the -bitcode
option if your app is using a bitcode build and will be submitted to the Apple App store with bitcode. This is only necessary if the SDK being used has not already been marked as being in bitcode mode. This normally happens when you first download the SDK using the -bitcode
to obtain the version including bitcode. However, this option may be used to ensure that this mode has definitely been selected for the SDK used.
If you are registering an Android APK that will be released using Google Play App signing then you must use the -playSigned
option, and add your app signing certificates. If the app is in the form of a .aab
then this option is set implicitly.
If you are registering an Android App Bundle (.aab
file) then the approov
tool needs access to bundletool
. The installation section describes how to get this tool. Use either the APPROOV_BUNDLETOOL
environment variable or the -bundletool
parameter with the registration command to specify the location of the bundletool
jar file. Note also that you must use an SDK that is version 2.3.0 or later.
If your Android app bundle includes other native libraries that include architectures that are no longer supported, then you may wish to remove them from your build and only include the relevant architectures using ABI Filters. For instance, you can use abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
to restrict to Approov supported architectures only. We have had reports that without this an incorrect package of native libraries can be delivered to certain devices and cause the Approov libapproov.so
to be missing and thus the Approov SDK cannot be initialized.
It is only possible to have a maximum of 250 registrations at any one time.
If you are registering an Android app that you intend to publish to the Play Store, you must ensure that it is signed with a V2 signature (or later) or else the registration will not match the Play Store version and it will not receive valid Approov tokens.
By default an app registration is permanent. This also means that an admin role is required to remove it. Removal needs to be restricted since an accidental removal will stop apps in production from receiving valid tokens, which may effectively bring an app service down. Note that in such a circumstance the Approov failover system will not be enabled, since this is not an Approov cloud system failure but explicit user action.
A temporary registration is performed just like a permanent registration, but with the addition of the -expireAfter
option.
$ approov registration -add my_app.ipa -expireAfter 3d
registering app MyCoolApp
P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=my.app.com-2.0[3]-2974 SDK:iOS(2.5.0)
registration successful, expires 2019-06-01 16:27:01
This creates a registration that is active for 3 days. The expiry time is given in local time in the output. Note though, if the registration already exists and is permanent or has a longer lived temporary expiration time, then that later time is retained.
The -expireAfter
parameter takes a duration which may be specified in y (years), d (days), h (hours), m (minutes) or s (seconds). Multiple time units may be used as long as they are specified in this order, e.g. 3d12h to register for three and a half days.
If you have made a Temporary Registration you may subsequently want to make that registration permanent, or at least to extend the time of the registration expiry. You can do this as follows, using the registration signature provided from the registration -list
option:
$ approov registration -updateExpiry P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=my.app.com-2.0[3]-2974
registration update successful
This method does not require you to have access to the original app package. Note that the -expireAfter
option can also be used to extend an existing temporary registration.
By default an app registration is for a specific version of the app. This means that a new app registration is required each time a new app is released. This gives the flexibility of fine grain control of your registrations, allowing you to remove individual registrations associated with specific versions (perhaps if you no longer wish to issue valid Approov tokens to an oldler version of your app).
However, it is possible to create an app registration that is not specific to a version. This will match any app issued with the same signing authority and with the same package name. If you use this style, then only one registration is required and any updated app release will also match. Remember that you lose the flexibiliuty of revoking individual versions in the future.
An unversioned registration is performed with the addition of the -unversioned
option. Note that this is not supported for Android .apk
packages, only for .aab
.
$ approov registration -add my_app.ipa -unversioned
registering app MyCoolApp
guwtV9sOk0AaEqE08mWKu32X6x8HuyBqMalrN2UcD64=my.app.com-*-5647 SDK:iOS(2.6.0)
registration successful
This creates an unversioned registration, denoted by the *
in place of the version number.
Unversioned registration is only supported in version 2.6.0 and later SDKs.
All of the registrations that are active for the account can be listed as follows:
$ approov registration -list
3 app registrations:
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974 SDK:Android(2.5.0) registered:2019-05-15 18:03:17
P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=approov.io.client.swift.shapes-client-1.0[1]-4803 SDK:iOS(2.5.0) registered:2019-05-15 18:03:31
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974 SDK:Android(2.5.0) registered:2019-05-29 14:49:36 expiry:2019-06-01 14:49:36
Each registration entry provides its overall signature as a single text block at the start of each line. This is the signature that must be copied in order to remove a registration (see next section). It contains a signature hash of aspects of the application, the application package name, application version information, and the ID of the Approov SDK that is integrated within it.
More information is also provided about the type and version number of the Approov SDK used. The registration time of the app is also provided (in local time). This is updated if a new registration of an app is made that happens to have the same signature. Finally, the expiry time of the registration is shown if it was registered with -expireAfter
. Once a registration expires it is automatically removed from the list.
Note that if you are looking for a particular registration you are able to find it by simply piping the output through grep
, for instance on Linux/MacOS:
$ approov registration -list | grep iOS
P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=approov.io.client.swift.shapes-client-1.0[1]-4803 SDK:iOS(2.5.0) registered:2019-05-15 18:03:31
Using this method it is possible to find registrations for specific platforms, SDK versions, app names and versions.
Any individual registration can be removed using its overall signature provided from the registration -list
option. For instance, to delete a temporary app registration:
$ approov registration -remove Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974
app registration Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974 has been removed
If you are using the zsh
shell you will need to enclose the -remove
parameter with single quotes ('
). This is because zsh
interprets square brackets as a globbing specifier.
You will be asked for confirmation if the app has a permanent registration, but not for a temporary registration. When a registration is removed no further valid Approov tokens will be issued for the app, with a delay of up to 30 seconds for this deletion operation to propagate through the Approov servers. Note that running apps may already hold a cached Approov token so it may take up to 5 minutes more before all valid token usage by those apps stops.
Permanent app registrations are removed in the same way but users are also asked to provide confirmation before the deletion occurs. An admin role can be used to remove any registration. Registrations that were added with an admin role can only be removed with an admin role (it doesn’t need to be the same user). A user’s dev role can be used to remove registrations that they added using a dev role. In other words, a developer can remove their own permanent registrations but not those added by other users.
$ approov registration -remove P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=approov.io.client.swift.shapes-client-1.0[1]-4803
WARNING: removing app registration will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
app registration P6nTmI3fhftxUr740ZnUMGKuAyTYjxP5dxKzJGd9yOk=approov.io.client.swift.shapes-client-1.0[1]-4803 has been removed
You can provide just the first part of a signature if it is unique amongst all of the registrations. If you are using the zsh
shell then this also means you don’t need to use single quotes if you don’t include the part of the signature with square brackets.
A facility is provided to remove multiple registrations using a single command. This is useful for maintaining the hygiene of your app registrations. You should actively remove registrations that are no longer in use, either because they are registrations for old testing apps (that have been accidentally permanently registered), or for previously released production apps that no end users are still using. The command is invoked as follows:
$ approov registration -removeMatching com.criticalblue.demo,30d
2 matched registrations to be removed:
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974 registered:2019-05-05 16:01:46
A4rcdXpfEOg0wse4snbAURnjRZidPvY63EULzUrZeFE=com.criticalblue.demo-3.0[4]-2974 registered:2019-05-15 18:02:08
WARNING: removing app registrations will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
removing Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-2.0[3]-2974
removing A4rcdXpfEOg0wse4snbAURnjRZidPvY63EULzUrZeFE=com.criticalblue.demo-3.0[4]-2974
This needs an administration role as this is a risky operation that could cause current production app registrations to be removed if care is not taken. The parameters provide the exact package name of apps to be matched and the minimum age of last registration of the app. In this case it is 30 days i.e. in this case, the command will only remove apps whose last registration is older than 30 days ago. A list of matching registrations is provided. You should review this carefully before allowing the operation to complete.
It is not necessary to go through the Play Store console to test using an app bundle with Approov. You can use Android Studio ‘Build → Build Bundle(s)/APK(s) → Build Bundle(s)’ to build an app bundle which will be automatically signed with the debug key from your debug keystore (debug.keystore
) or you can use ‘Build → Generate Signed Bundle / APK…’ to build an app bundle and sign it with a key from another key store.
Locate the signed app bundle you have just built and follow the bundletool
instructions on Generate a set of APKs from your app bundle and Deploy APKs to a connected device to deploy the app bundle to a test device. The installation section describes how bundletool
can be obtained.
For Approov to recognize app bundles built and deployed in this way, you need to add the app signing certificate to Approov, for the key that you used to sign the app bundle. The app signing certificate can be extracted from the key store using the Java keytool
, which comes as part of your Java installation (Java can be installed from here). Issue a command such as this, substituting the key alias and filenames to match the key store you are using.
$ keytool -exportcert -keystore debug.keystore -alias androiddebugkey -file androiddebugkey-app-signing-cert.der
Enter keystore password:
Certificate stored in file <androiddebugkey-app-signing-cert.der>
Then, using the approov
tool, add the exported app signing certificate as described above. Keep in mind that the number of app signing certificates in Approov is limited to 10. Sharing of debug or development keystores among a development group is advised.
A facility is provided to add an arbitrary string instead of the version name for a particular registration. This allows a particular registration to be annotated to show that it is for some specific purpose (perhaps a special testing version, for instance) and this allows it to be easily located in the list of all app registrations. We advise you not to use this facility on apps which are to be retained for production purposes, so that the real version number of app registration is available.
The annotation is added as follows:
$ approov registration -add my_app.apk -versionName "special"
registering app
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=my.app.com-special-1041 SDK:Android(2.0.4)
registration successful
This particular registration can then be easily found again:
$ approov registration -list | grep special
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=com.criticalblue.demo-special-1041 SDK:Android(2.0.4) registered:2019-05-29 17:07:22
By default, the SDK library ID for an app is determined by the approov
tool that analyses the app to be registered. In some cases though an Approov SDK may itself be embedded inside another SDK within the app. In this case the tool cannot automatically locate the SDK and, in any case, it is possible for there to be more than one Approov SDK in the app.
In such a case you must use the specific ID of the Approov SDK that you want to register with. This can be specified with an additional command line option as follows:
$ approov registration -add my_app.apk -libraryID 1056
registering app
Ac15BRFWqxn79dGsjOdVJXVqBQQ64ZWTAuKdrzRC9hc=my.app.com-2.0[3]-1056 SDK:Android(2.0.2)
registration successful
This feature allows a registration to be made when access is not available to the original IPA or APK. This might be necessary if an app registration has been accidentally deleted and the original IPA or APK file is no longer available, for instance. It leverages the getting specific device informaion capability. Please follow those instructions in order to be able to get the detailed information for a running device, including its app registration information.
You can then add the registration as follows:
$ approov registration -addFromDevice qZka0yfv+ExvOq3PRh6pGw==
using app information captured at 2021-04-07 18:30:10 BST
registering tL8CNNpf4XlekWkwuyJaCRtcntJyjZ1ujYJ7CZMO+Fk==com.criticalblue.demo-3.0[3]-4000
registration successful
This adds the registration associated with the app that is running on the device. Note that is is also possible to use the -expireAfter
option to create a temporary registration using this method.
It is is possible to use the -playSigned
option to provide a registration that is compatible with an Android app that uses Google Play signing. An app released as an App Bundle will always use Google Play signing. Note that in this case you must also add your app signing certificates.
A registration cloning option is provided to support a use case employed by certain Approov customers. In this use case the developer and publisher of the app using the Approov SDK (developer) is distinct from the publisher of the API that is protected by Approov (controller). Both the developer and the controller must have Approov accounts. The developer is responsible for managing app registrations. The controller is responsible for Adding API Domains and also Managing Pins. Only the controller is able to view and manage the secret keys associated with their APIs.
To use this flow, an admin
role holder of the controller account should create a delegate
user role for use by the developer. The developer can then use this role to:
The developer uses the configuration obtained from the controller’s account to initialize the SDK in the app. Since the account that the app uses depends on the SDK configuration with which it is initialized, this flow means that the Approov SDK will use the controller’s account when it is obtaining tokens to include on its API requests. Typically, the app will provide a way to dynamically select which initial configuration is used, allowing the app to either use the developer’s or the controller’s account. In general, there might be several possible controller accounts for a single app.
While in app development, the developer can use Approov in the normal way using their own Approov account. When they do a release for use with the controller’s APIs, either for testing or production, then they will need to copy the active app registrations from their account into the controller’s account. In the case where a single app is to be used by various controllers, the app developer will need a delegate
role provided by each one of those controller accounts. If the app has the ability to dynamically switch which controller account is to be used, then the SDK must be reinitialized dynamically to select the appropriate one.
If the developer has initialized the delegate
role(s) into their Approov CLI then the registrations can be cloned as follows:
$ approov registration -cloneToAllDelegateAccounts
registrations will be cloned to the following accounts:
accountA
accountB
WARNING: this will have an immediate impact on production of the target accounts
ATTENTION: If you wish to continue then please type YES and return: YES
cloning 3 permanent registrations into account accountA
cloning 3 permanent registrations into account accountB
If the delegate
roles are password protected then you may be asked for each of the passwords during the cloning operation. If you are unable to provide the correct password then the cloning for that particular account is skipped.
In the example, three registrations are cloned to two different accounts. Note that only permanent registrations are cloned. Note also, due to the complete cloning of all active app registrations, if a new API controller account is introduced, with a new delegate
role, then all registrations (including historic ones) are easily cloned with this single command. If a registration was previously cloned, but then subsequently removed, then the next clone operation will also remove it from the target delegate account.
When the registrations are listed in the delegate account they are associated with the name of the app developer account from which they were cloned. This allows the API controller to easily identify the apps that were contributed by different delegate roles.
If you only want to clone to a subset of all your delegate
role accounts, then you can do this as follows:
approov registration -cloneToAccounts accountA
registrations will be cloned to the following accounts:
accountA
WARNING: this will have an immediate impact on production of the target accounts
ATTENTION: If you wish to continue then please type YES and return: YES
cloning 3 permanent registrations into account accountA
This option takes a comma seperated list of the accounts into which you wish to clone your registrations.
The maximum number of registrations that may be cloned in this way is 50. A warning is provided if the maximum number of registrations in the target account is exceeded.
This command only clones the app registrations. If you are using Google Play signing then you will also need to set the app signing certificate in the target account. The delegate
role provides rights to do this. Note also that if you are relying on device check or safety net this information can also be set using a delegate
role.
The result of performing a token fetch request with the Approov SDK is an Approov token held by the app. The app then adds the token to the requests made to the backend API to be used in the authorization flow. This section describes the format and content of the tokens.
The Approov service uses JSON Web Tokens (JWTs) to represent the authenticity of client apps. This in an open and standard mechanism for representing claims in a tamper proof form and your web service will need to decode and verify these tokens as part of the Approov flow. An Approov token is typically a JWS (a type of JWT) which consists of three parts; the header, payload and signature where each part is base64url encoded and the parts are separated by periods. For a more in depth explanation off JWT tokens, please read this introduction.
JWT libraries take responsibility for generating the header and the signature. We just need to specify the signing algorithm we want to use, the secret key for the algorithm, and the un-encoded payload part. For JWTs, the payload is always a JSON object with the entries referred to as claims.
This section describes the approach used to create signed JWTs (called JWS to differentiate them from JWE tokens which are encrypted). If an app has passed the attestation process acoording to the selected rejection policy, then a correctly signed token is issued. If the rejection policy causes the app to fail attestation then a token is still issued, but it will not be correctly signed. An observer that does not know the signing secret key will not be able to differentiate between these two cases.
There are a number of different options for token signing depending on the type
notified for the API domain when it is added:
account
: This is the default, and this selects a token signed with the HS256
algorithm. This peforms HMAC signing using the account secret key. The 512-bit symmetric secret may be exported using the approov
CLI. If there are multiple API domains using the account type then they are all signed using this key.keyset
: If keyset keys have been added then an API domain may be assigned to use one of them for token signing. Each key in the keyset is associated with a particular signing algorithm, providing support for asymmetric key pairs such as RSA and ECDSA.restricted
: Some specific domains are considered to be restricted and are managed directly by Approov (such as the demonstration Shapes domain). If these API domains are added to the account then they are automatically allocated the restricted
type. Approov tokens for them are signed or encrypted using a different key that is not accessible.Be aware of the performance impact when you are selecting your algorithm. Token signing is performed by the Approov cloud service and it impacts the time required to get a token. Token verification is performed by the backend that handles API requests. The precise performance for different algorithms will be specific to the implementation you use and the hardware on which it is running, however, you can use the table below as a rough guide for Approov tokens (using an HS256 base-case with a signing time of x
):
Algorithm | Signing | Verifying |
---|---|---|
HS256 | x | 2x |
ES256 | 5x | 15x |
RS256/PS256 | 250x | 10x |
Note that Approov can also support another type of JWT, encrypted JWE tokens. Typically these only need to be used in conjunction with the Offline Security Mode. However, they might also be used if you wish to provide extended information within the anno and do not want to reveal the contents to any party that is able to read the tokens. JWE tokens use compact serialization which has 5 parts separated by .
, where the first part is unencrypted and holds the encoded information about the algorithms used to encrypt the Content Encryption Key (CEK) and the payload. The Approov service uses A256GCMKW
(alg
claim) for encrypting the CEK and A256GCM
(enc
claim) for content encryption.
An API domain will use JWEs if the -jwe
option is used when adding the domain. A JWE will also be used if a keyset key is used that is associated with the A256GCMKW
algorithm.
The normal lifetime for an Approov token is 5 minutes, plus a grace period, from the point of issue by the Approov cloud service. The grace period of a few seconds is added to allow a valid token to be propagated and checked within a backend API system. If a particular device is identified as being risky in some way (such as it being rooted or jailbroken), then, independently of the overall security rejection policy that is set, it will receive a shorter lived 2 minute token instead of a 5 minute one. This forces the device to make more frequent checks as it receives tokens.
The app itself should never cache Approov tokens that it gets from the Approov SDK. Instead, a fresh call to fetchApproovToken
or fetchApproovTokenAndWait
should be made each time an Approov token is required. If a token has already been fetched, and is not yet expired, then it will be returned immediately. However, each fetch call does some basic app environment checking and, if issues are discovered, performs a complete attestation check to fetch a new token. Thus an Approov token lifetime should be considered to be up to 5 minutes, since any cached token may be discarded at any point.
Depending on the source of an Approov token (the main Approov service or the Failover) the claims it contains may vary. However, the expiry (exp
) claim is always present. The details of the claims are provided in the table below:
Key | Name | Type | Description |
exp
|
Expiry | Number | The only mandatory claim for Approov tokens. It specifies the expiry time for the token as a Unix timestamp. |
did
|
Device ID | String | This claim identifies the device for which the token was issued. This is a base64 encoded string representing a 128-bit device identifier. Note that this is not, strictly speaking, a device identifier as it is also influenced by the app identifier and may change if the same app is uninstalled and then reinstalled on the same device. |
arc
|
Attestation Response Code | String | This is an optional claim that encodes information about a subset of the device property flags and also whether the attestation was a pass or fail. The claim is encoded in base32 and is typically 10 characters long (although it may be longer in some circumstances). This claim is not included by tokens from the failover. |
ip
|
IP Address | String | This holds the IP address of the device as seen by the Approov cloud service. It is provided in a human readable IP address format (in either IPv4 or IPv6 format). In practice this value can often change between the time a token is issued and the time it is sent to your backend, so you should never block if it differs, but you may include it as a signal that tokens have somehow been stolen and are being replayed. This claim is not included by tokens from the failover or if the IP Tracking Policy for the account has been set to none .
|
iss
|
Issuer | String | An optional claim that is added if the issuer inclusion option is enabled. This provides the Approov account ID that was used to issue the token (suffixed with approov.io ). It can be used as an additional layer of backend verification if signing keys are shared between multiple accounts. It indicates that tokens were issued from the expected Approov account. This claim may also be set to an explicit value for long lived Approov tokens. This flexibility is designed for use with server-to-server communication (which may only be signed with the account specific secret keys).
|
anno
|
Annotation | Embedded JSON | This is an embedded JSON array of strings showing the list of flags that are set and are in the annotation set for the security policy that is selected. This allows additional information to be collected about the state of a particular device without necessarily causing an attestation failure. Note that if there are no possible annotations then this claim is not present at all. This claim is not included by tokens from the failover. |
pay
|
Payload Hash | String | An optional claim that is added if the protected app passes a token binding argument to the setDataHashInToken method. The claim value is set to the base64 encoded SHA256 hash of the provided payload string. This is typically used to bind an Approov token to some other data used by your app to enhance security (like a user auth token). |
aud
|
Audience | String | An optional claim that is added if the audience inclusion option is enabled. This provides the domain for which the token was issued. It can be used as an additional layer of backend verification to ensure that tokens intended for one domain cannot be used on to access a different one. |
mskid
|
Message Siging Key ID | String | This is an optional claim that encodes the ID of a key being used for Message Signing. This is only present in Approov tokens for which message signing is active. This claim is not included by tokens from the failover. |
mpk
|
Measurement Proof Key | String | An optional claim to provide the measurement proof key if a measurement has been requested by the SDK on the domain for which the token is issued. This is a base64 encoded 128-bit proof key value. Note that if measurement is being used, then JWE tokens will be used to keep this claim secret. |
imh
|
Integrity Measurement Hash | String | An optional claim to provide the integrity measurement hash if a measurement has been requested by the SDK on the domain for which the token is issued. This is a base64 encoded 256-bit SHA256 measurement value. Note that if measurement is being used, then JWE tokens will be used to keep this claim secret. |
dmh
|
Device Measurement Hash | String | An optional claim to provide the device measurement hash if a measurement has been requested by the SDK on the domain for which the token is issued. This is a base64 encoded 256-bit SHA256 measurement value. Note that if measurement is being used, then JWE tokens will be used to keep this claim secret. |
An Attestation Response Code (ARC) is a short alphanumeric code that is assigned to all Approov token fetch events and is embedded inside the received Approov tokens in the arc
claim. The ARC encodes a subset of the device property flags of the device making the request. It also encodes whether a valid Approov token is being issued or not. This is the preferred way of finding out why an attestation may have failed. In particular, this is invaluable for support situations when it is necessary to determine why a particular user is not receiving valid Approov tokens. It may also be useful for internal debug purposes or for tracking flags that are set on both valid and invalid tokens.
You can enable this option as follows:
$ approov policy -setARC on
WARNING: updating the arc policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
arc policy was set successfully
Changing the ARC policy requires an admin role and confirmation.
Although you are able to set the arc
policy to off
we do not recommend this.
The current status of the ARC policy can be obtained as follows:
$ approov policy -getARC
ARC policy is on
flags that can be ARC encoded:
app-not-registered
bad-hmac
...
If the policy is on then a list of the device property flags that can be encoded in the ARC are provided. This means that if any of those properties were set for the Approov token fetch then you will be able to determine that from decoding the ARC.
Accounts created since the release of version 2.5 have this option enabled by default.
The codes are encoded using a cryptographic secret so that it is not possible to determine their meaning without access to the Approov account backend. Moreover, the encoding for each Approov account is unique so codes generated by one account cannot be decoded by another.
The Attestation Response Code is embedded in the arc
claim of all issued tokens. However, if you wish to obtain the code directly then a method in the SDK is provided that is available once an Approov token is fetched. This is available even if the Approov token is a JWE
and thus encrypted. Use the following call:
String arc = approovResult.getARC();
let arc = approovResult.ARC
If the Approov token fetch was unsuccessful, or the ARC capability is not enabled, then an empty string is returned. Otherwise the short base32
encoded ARC string is provided. It is safe to include this in logging, since without access to the Approov backend it is not possible to decode its contents.
The ARC can be provided in error information presented to the user or submitted by the app in an API call, for cases where the user is having trouble using their app. The base32
encoding and short length means that it is possible to read out or submit this information reliably for support purposes. The encoding also includes internal checking that detects most transcription errors. This facility might be needed if the app is not usable because invalid Approov tokens are being issued. When the ARC is decoded it is then possible to ascertain why Approov token fetches are failing and for remedial action to be taken.
This method is only available in version 2.5.0 and later SDKs.
An API endpoint is provided that takes an ARC and returns the decoded information for it. This is designed for use in backend customer support systems. Information about how to access the API endpoint is provided as follows:
$ approov token -showArcInfoCurl
curl -H "Authorization: Bearer xxx..." -H "Arc: <arc>" https://<account-domain>/arc-info/
The API key in the Authorization
is account specific and should not change unless a specific support request is made to request its rotation. The API key only allows the decoding of tokens and obtaining additional token information and does not enable any other actions on the account. However, as with any account credential, steps should be taken to keep it secured and to prevent accidental disclosure or inclusion in source code repositories.
An example API request is made as follows, with the ARC XJEUXVAKZY
:
$ curl -H "Authorization: Bearer xxx..." -H "Arc: XJEUXVAKZY" https://<account-domain>/arc-info/
{
"status": "failed",
"deviceProperties": [
"app-not-registered",
"rooted",
"root-risk",
"xposed"
]
}
The status
indicates if the attestation was passed
or failed
. If the provided ARC is invalid in some way (perhaps issued to a different Approov account or entered incorrectly) then the status will be arc invalid
.
The deviceProperties
lists device property flags that have been determined from the device. If the status is failed
then this will almost certainly pinpoint the reason(s).
Note that, although the output is a curl
command, you can of course use an alternative tool or command to perform the GET
request.
This API is for the use of your backend systems only. Never build calls to this API, and especially not the Authorization
key required, directly into your mobile app. Moreover, this endpoint should only be used for informational purposes, as it may be subject to rate limiting and does not have the Cloud Server Redundancy features of the Approov token issuance process.
You should never use this endpoint to check the validity of the token. Always check validity by verifying the signature of the Approov token directly. This endpoint should only be used for informational purposes on a subset of received tokens; it may be subject to rate limiting and does not have the Cloud Server Redundancy features of the Approov token issuance service.
An API is provided that allows additional information to be obtained for a previously issued Approov token. Note that for additional information to be available the Attestation Response Code feature must be enabled.
Information about how to access the API endpoint is provided as follows:
$ approov token -showTokenInfoCurl
curl -X PUT -H "Authorization: Bearer xxx..." -H "Approov-Token: <token>" https://<account-domain>/token-info/
add `-H "Report-Message: <message>" to report a particular token
The API key in the Authorization
is account specific and should not change unless a specific support request is made to request its rotation. The API key only allows the decoding of tokens and obtaining additional token information and does not enable any other actions on the account. However, as with any account credential, steps should be taken to keep it secured and to prevent accidental disclosure or inclusion in source code repositories.
You should provide an issued Approov token to the API endpoint, for example:
$ curl -X PUT -H "Authorization: Bearer xxx..." -H "Approov-Token: eyJh..." https://<account-domain>/token-info/
{
"did": "qZka0yfv+ExvOq3PRh6pGw==",
"status": "failed",
"claims": {
"arc": "B3Y5SLMO2K",
"did": "qZka0yfv+ExvOq3PRh6pGw==",
"exp": 1607528162,
"ip": "1.2.3.4"
},
"expiryTime": "2020-12-09 15:36:02 UTC"
"deviceProperties": [
"app-not-registered",
"rooted",
"root-risk",
"xposed"
]
}
The status
indicates if the attestation was passed
or failed
. If the provided token was issued to a different account then the status is likely to be arc invalid
(and if it does happen to be valid it will not report the correct decoding). The did
property provides the Device ID. The full set of token claims
are provided, even if the Approov token is an encrypted JWE
. The expiryTime
provides a readable form of the expiry time of the Approov token. Note that, the tokens you provide may have expired and this will not impact their status or the ability to decode them.
The deviceProperties
lists the device property flags that have been determined from the device. If the status is failed
then this will almost certainly pinpoint the reason(s).
Note that although the syntax is provided for curl
for your convenience, any standard tool or mechanism can be used to perform the PUT
request to the endpoint.
This API is for the use of your backend systems only. Never build calls to this API into your mobile app and, especially, never expose the Authorization
key.
You should never use this endpoint to check the validity of the token. Always check validity by verifying the signature of the Approov token directly. This endpoint should only be used for informational purposes on a subset of received tokens; it may be subject to rate limiting and does not have the Cloud Server Redundancy features of the Approov token issuance service.
The Token Information endpoint provides an optional facility to report the misuse of particular tokens. This endpoint can be used in conjunction with Approov support to allow automatic reporting of tokens that appear to be being misused in some way. This may be because you have reason to believe that a valid Approov token is being issued when it should not have been. Alternatively, if you believe that the Approov token has been somehow extracted from a running app and is being used to make API calls outside of the official app then these should be reported.
You should include a short message (256 characters or less) describing the issue in the Report-Message
header for the request, for example:
$ curl -X PUT -H "Authorization: Bearer xxx..." -H "Approov-Token: eyJh..." -H "Report-Message: Expected a token fail on this device, contact ops@mydomain.com" https://<account-domain>/token-info/
{
"did": "qZka0yfv+ExvOq3PRh6pGw==",
"status": "failed",
"claims": {
"arc": "B3Y5SLMO2K",
"did": "qZka0yfv+ExvOq3PRh6pGw==",
"exp": 1598608042,
"ip": "1.2.3.4"
},
"deviceProperties": [
"app-not-registered",
"rooted",
"root-risk",
"xposed"
]
}
Token reports are automatically forwarded to Approov support and they will be notified at the end of day in which the report is made. Unless you are already in dialogue with Approov support about this issue, we suggest you include email contact information in your report message. Approov support will analyze the information provided, and may gather more information from the specific device reported, and will respond with some additional diagnosis of the issue.
Token reports are rate limited, typically restricted to one per minute. If you exceed the rate limit then the API will return Ok
but the report will not be forwarded to Approov support. Note also that subsequent token reports are ignored if they are issued on the same day and about the same device ID.
A checking option is provided that allows any Approov token to be checked. This is useful for debug purposes during your integration with Approov. Simply use:
$ approov token -check eyJhbGciOiJIUzI1Ni…
failed: expired alg:HS256 {"anno":["app-not-registered"],"did":"h4gubfCFzJu81j/U2BJsdg==","exp":1558626228,"ip":"1.2.3.4","pay":"6ZIvH1YelZyc2hx1iRRqD3dWNcFgjVtCon+7921XDjg="}
The Approov token content is decoded, and information is provided about whether the token is passed
or failed
. For a JWS it also shows if the token has expired or not. Remember that expiry and signature validity are independent. The validity of a token (i.e. whether it is signed with the account secret key) depends on whether the request came from a valid app instance and also the security policy in place in the account. The expiry is purely related to how long ago the token was issued. Tokens normally only have a 5 minute lifetime.
The signing algorithm of the token (alg
) is also shown, as well as any key ID (kid
) in the token.
The content of the decoded token will depend on other Approov features that are being used. This particular token is issued from an account with a non-default annotation policy, that provides some reasons why a token may be invalid, in the anno
claim. It also has a pay
claim as a result of using setDataHashInToken
.
Note that if Attestation Response Codes are enabled for the account then this command will also provide a list of the device property flags associated with the device to which the Approov token was issued.
Note that the token check is also able to process encrypted JWE tokens. It shows if they are valid and can decode their content, even if they are invalid.
It is generally not recommended to log Approov tokens within an app. This is because it might be possible for an attacker to turn logging on in a production app and use this as a mechanism to steal valid Approov tokens that have been received. Furthermore, the raw Approov tokens are also very opaque since they are just base64url encoded JWT strings and therefore not very useful for debugging.
A loggable token getter is provided in the SDK to Token Fetch Interface provided as the result returned from fetchApproovToken
or fetchApproovTokenAndWait
. This provides a decoded string form of the Approov token, and is therefore much more useful for debug purposes. The returned string holds a JSON object containing the payload contents with an additional sip
claim, as demonstrated by the following example:
{
"anno": [
"app-not-registered"
],
"did": "h4gubfCFzJu81j/U2BJsdg==",
"exp": 1558626228,
"ip": "1.2.3.4",
"pay": "6ZIvH1YelZyc2hx1iRRqD3dWNcFgjVtCon+7921XDjg=",
"sip": "HggK-u"
}
Note that if no valid token was returned from the Approov token fetch request, or the returned token was a JWE, then a marshaled JSON string giving the error is provided, e.g.:
{
"error": "POOR_NETWORK"
}
{
"error": "JWE"
}
As mentioned, a loggable token includes a special sip
claim. This contains the first few characters of the signature of the token. Six characters are provided in base64, giving 36 bits out of the 256 bits provided in the complete signature. Thus it is not sufficient to create a validly signed token, but, if the signing key is known, it is sufficient to check, with a very high degree of accuracy, whether an Approov token is valid. Note that app itself never knows the symmetric secret used for signing tokens. Its possible to check a loggable token in the same way as a JWT as follows:
$ approov token -check '{"anno":["app-not-registered"...'
failed: loggable
Note that the loggable token must be enclosed in single quotes so that it is treated properly as a single command line parameter.
A statement is provided about whether the token was valid or not. It does this by reproducing the complete Approov token on the cloud service with the correct signature and then checking this against the prefix part of the signature that it knows from the sip
.
The sip
claim cannot be used to determined token validity if the token was signed with a the probabilistic signature scheme. Probabilistic schemes include keyset key that use one of the following algorithms: PS256
, PS384
, PS512
, ES256
, ES384
or ES512
. For these algorithms the loggable token will always be interpreted as a fail.
If you are using a version of the SDK prior to 2.2.0 and have enabled Key IDs, attempting to get a loggable token will result in a BAD_JWT_HEADER
error.
It is possible to generate Approov tokens that have a long expiry time. Approov tokens issued via the Approov SDK only have an expiry time of 5 minutes or less. This facility allows tokens for an arbitrary duration to be created. It requires an admin role. To create one use:
$ approov token -genLongLived server-token,90d
token will expire at 2019-06-03 13:24:38
WARNING: Long lived token should never be integrated into public clients and their security must be carefully managed
eyJhbGciOiJIUzI…
This generates an Approov token that is valid for 90 days. The server-token
parameter is included in the iss
claim of the token. Change this to whatever name you would like. It can be used to identify particular long lived tokens, or even to revoke certain ones by backend API checking. In effect this claim could be used as a type of API key within the overall signed Approov token. The expiry time should be specified in days and cannot be less than 1 day.
The generated Approov token can be checked and decoded as follows:
$ approov token -check eyJhbGciOiJIUzI…
passed: alg:HS256 {"exp":1567078473,"iss":"server-token"}
Long lived tokens can be used to authorize communication to the protected backend API for server-to-server communication and test environments (such as Postman). This allows a particular API to be accessible both from mobile apps and also via other servers without having to create a different authorization mechanism. Generating a long lived token has no impact on the expiry time of tokens issued normally through the Approov SDK.
Long lived tokens must never be included in public clients, such as web pages or mobile apps. They should only be used in circumstances where the token can be protected in a server environment.
Long lived tokens cannot be generated for restricted domains, such as shapes.approov.io
demo endpoint. Moreover, it is only possible to generate long lived JWS tokens signed with the account secret key. It is not possible to create tokens signed with a keyset key.
The IP tracking policy determines how Internet Protocol addresses are dealt with in your Approov account. When your account is setup the policy will be token
. This means that the IP address of the device requesting an Approov token will be included in the token itself in the ip
claim. However, in accordance with Approov’s privacy notice, no record will be kept of the IP address in Approov’s internal logs or records. This is because IP addresses can be considered to be Personally Identifiable Information (PII) and the Approov service does not collect this by default for an account holder’s end customers. IP addresses are only stored by Approov services after transformation through a one-way hash function from which the original IP address cannot be recovered.
If desired the IP tracking policy can be changed to none
so that the IP address claim is not even included in the issued Approov token.
$ approov policy -setIPTracking none
WARNING: updating the IP tracking policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
IP tracking policy was set successfully
An admin role and confirmation is required to make an IP tracking policy change.
A policy of full
is also available whereby permission is given for Approov services to record unhashed IP addresses. This allows more sophisticated correlation analysis between IP addresses and device characteristics, carried out in conjunction with Approov support. Note, however, that if this option is used then the Approov service is collecting PII on your behalf and this may have GDPR implications.
The current IP tracking policy can always be retrieved as follows:
$ approov policy -getIPTracking
IP tracking policy is none
The audience inclusion feature allows the aud
claim to be populated in all Approov tokens issued for the account. This will be set to the domain name for which the Approov token was issued. This can be useful to ensure that tokens issued for one domain cannot be used to gain access to a different domain that shares the same signing secret key. This needs to be checked by the backend API integration.
The audience inclusion feature can be enabled as follows:
$ approov policy -setAudience on
WARNING: updating the audience policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
An admin role and confirmation is required to make an audience policy change.
The current audience policy can always be retrieved as follows:
$ approov policy -getAudience
audience policy is on
Note that wildcard tokens are issued with an aud
claim of *
.
The issuer inclusion feature allows the iss
claim to be populated in all Approov tokens issued for the account. This will be set to the name of the account suffixed with approov.io
. This is useful as an additional verification if signing keys are being shared between multiple accounts. The backend API integration is able to verify that the token was issued for the expected account.
The issuer inclusion feature can be enabled as follows:
$ approov policy -setIssuer on
WARNING: updating the issuer policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
issuer policy was set successfully
An admin role and confirmation is required to make an issuer policy change.
The current issuer policy can always be retrieved as follows:
$ approov policy -getIssuer
issuer policy is on
This section describes the changes that are needed in the backend API to check that an Approov token is valid.
This section provides a detailed reference for backend integrations. If you able to use one of our Quickstart Backend Integrations then you may find this simpler.
To protect a backend API with Approov it is necessary to check that incoming requests have a valid Approov token. This might be required on all endpoints or just on some that demand additional security. Typically, token checking will be performed in some middleware layer of the backend rather than be implemented in the business logic.
Approov token checking is often performed in addition to user authorization, as an orthogonal activity. User authentication and authorization ensure that a known user is invoking the software making the request. Approov checking ensures that the software making the request is actually what it claims to be.
Approov uses standard JWTs as discussed in the previous section. This makes the backend API checking straightforward since almost all backend languages and technologies support JWT checking. To check an Approov JWS, the account secret key must be made available to the backend code. The token will be signed with the HS256
profile, and the shared symmetric account secret key is needed to perform the check. Libraries for working with JWTs are available in most common languages, with many listed on the JWT homepage.
There are essentially three cases which should cause a request to be rejected:
HS256
.Note that the actual behavior for an invalid token does not have to be rejection, although this is the typical approach. An invalid token event could simply be used for logging or as a signal to require some additional authentication and security checks on a particular user.
We recommend that the backend integration has a dynamic facility to enable or disable the Approov token check. This allows traffic to be passed through without the check in case of some emergency situation. Moreover, you may wish to collect logging for events where invalid Approov tokens are being provided.
The account secret key can be obtained with the following command:
$ approov secret -get base64
note: secret is base64 encoded and must be decoded to its binary form to verify Approov tokens
dEitzKUYJLQ…
An admin role required to gain access to the account secret key.
This example displays the secret as a base64
encoded string, representing a 512-bit value that is randomly assigned to the account during signup. The value of the secret must be protected with extreme care. The number of individuals with exposure to the value of the secret should be minimized and it should never be checked into a source control system. Ideally, it should only be made available to the runtime environment of the backend servers. Note that if base64url
is used as a parameter to the command then the secret can be obtained in base64url
encoded format, which may be needed by some backend integrations.
Note that it is also possible to obtain the secret in raw form, as discussed here.
If you wish to output the secret value, with no additional text, you may use the -plain
option. This allows the output to be easily piped into other command line tools, so that generic scripts can be easily developed that extract and operate on the account secret key. Of course, you should never include your administration token in such a script directly.
If you wish to export the secret key that is used for JWE tokens (of the account
type and using the A256GCMKW
algorithm) then the -jwe
option can be used as follows:
$ approov secret -jwe -get base64
note: secret is base64 encoded and must be decoded to its binary form to verify Approov tokens
1bgyVw6/9X6...
The account secret key may also be exported in JSON Web Key (JWK) format as follows:
$ approov secret -getJWK secret.jwk
JWK written to file secret.jwk
This writes the JWK to the file secret.jwk
as follows:
$ less secret.jwk
{
"kty": "oct",
"use": "sig",
"k": "dEitzKUYJLQ…",
"alg": "HS256"
}
The JWK format is useful for certain backend systems that accept this format directly. Usage with JWKs is also often associated with the requirement for a Key ID property in tokens. This needs to be set prior to getting the JWK so that the kid
property is listed.
It is also possible to export it in the JSON Web Key Set format, for systems that require that format. In this case the single account secret key is included in the keys
map.
If the -jwe
option is used then the secret key used for JWEs is provided, rather than the one for JWSs.
It is possible to generate example Approov tokens that have the same format as those generated for a mobile app. For example:
$ approov token -genExample my.api.io
eyJhbGciOiJIUzI1N…
The API domain, for which the token is being generated, must be provided in the same way that the app provides it for a call to fetchApproovToken
. The result is a valid token that lasts for one hour. The extended expiry time of these tokens provides for longer debug sessions using a single token. For example, the token can be used in synthetic queries made to a backend API to check that it is working correctly. There is no need to know the actual account secret key to use this facility as the token is automatically signed with the correct secret.
If an API domain is provided that has not been added for the account then no token can be generated. If the API domain is associated with encrypted JWE tokens then one is generated, to allow verification of the JWE decryption logic in the backend.
The example tokens contain a fake IP address and device ID that are in the correct format. A -setDataHashInToken
option is also provided for testing the Token Binding feature. You can decode the example Approov token to see what it contains:
$ approov token -check eyJhbGciOiJIUzI1N…
passed: alg:HS256 {"did":"ExampleApproovTokenDID==","exp":1558980988,"ip":"1.2.3.4"}
By default a valid Approov token is generated. But it is also possible to generate an invalid
token to test the backend API handling with such tokens. For instance:
$ approov token -genExample my.api.io -type invalid
eyJhbGciOiJIUzI1N…
$ approov token -check eyJhbGciOiJIUzI1N…
failed: alg:HS256 {"did":"ExampleApproovTokenDID==","exp":1558981918,"ip":"1.2.3.4"}
The account secret key can be changed with the following command:
$ approov secret -change
WARNING: updating the account secret key will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
This requires an admin role and a confirmation of the operation. If confirmed then the new account secret key value is output as follows:
new base64 encoded account secret key is VcXl2IgVUgz…
If the account secret key is changed then this has an impact in production within 30 seconds for newly issued tokens. This means that for a period of up to 5 minutes the backend API may see a combination of tokens signed with the old secret (if they have been cached and reused by the app SDKs) and new ones signed with the new secret (for freshly obtained tokens).
In general it is hard for a backend integration to deal with two different secret values, so we recommend the following procedure if rotating the account secret key:
There are certain specific backend JWT checking libraries that require the secret to be specified as a raw string (they have no facility to provide it in an encoded form such as base64
). To support this, a feature is provided to get the secret in its raw format. However, the initial secret generated for your account will almost certainly contain some bytes that are not printable and therefore cannot easily be added to a string. Thus, if you need the secret in its raw form, you must first change it and include the special -forcePrintable
option as follows:
approov secret change -forcePrintable
It is then possible to obtain the raw secret value as follows:
approov secret -get raw
Use of this option reduces the entropy of the secret and so you should only use this facility if there is no option but to use a JWT library that requires the secret in this format. Typically you should specify the secret in an encoded format rather than a raw string.
This section describes how certificate public key pins can be defined so that they can be made available in the initial and dynamic configuration of the apps using Approov. The next section deals with how the app can use this pinning information.
It is essential that all of the channels over which Approov tokens may be transmitted use pinned connections. This means that the app should only be willing to establish a TLS connection where the presented certificates are both valid and where at least one public key of a certificate in the chain is specifically white listed (aka pinned), by the app itself, as being valid.
Pinning is necessitated by the fact that an attacker may be able to control both the device and also the channel over which it communicates. In the absence of pinning, attackers are able to install additional certificates on the device as being trusted, and then use a proxy to decrypt any traffic from the mobile app to the API endpoints. This method can then be used to steal valid Approov tokens from the communication channel in order to make spoofed requests, as though they were coming from the app.
Pinning represents good security hygiene. It prevents real users from having their traffic intercepted if an attacker is able to trick them into installing an additional certificate on their device that then becomes trusted for TLS communication. Pinning ensures that the app avoids completely delegating its trust to the device.
There are various existing mechanisms to implement pinning in an app. Although conceptually identical, there are significant implementation differences between Android and iOS:
OkHttp
stack is straightforward using the CertificatePinner class.These standard methods can be used to pin an app’s connections with the backend API server.
Note that the Approov SDK uses its own methods to pin the connections it makes with the Approov cloud service, so you can be sure that this channel is defended without any further work.
Approov provides a dynamic pinning mechanism, discussed in the next section. This section discusses some of the limitations of traditional static pinning.
Under normal circumstances public key pins do not have to be changed very often. Approov uses public key pinning rather than certificate pinning. This means that the pin is actually to the public key of the certificate rather than a hash of the whole certificate contents. The advantage of this is that, if certificates are changed simply because they are expiring and need to be renewed, then the same public/private key pair may be used to generate the new certificate without invalidating the pins.
The difficulty arises when a certificate has to be revoked and replaced if there is a concern that the private key has been compromised (or lost). This could allow an attacker to generate fake certificates and spoof the endpoint, intercepting traffic from the app if they are able to insert themselves as a Man-in-the-Middle (MitM) in the network.
Such an event necessitates a change in the public key pin for the certificate, as a new private key needs to be generated. If this is simply changed immediately then it would prevent apps with the public key pin from connecting to the API. The app would no longer work. It is recommended practice to also include a backup pin inside in the app that could be used in such an emergency. But this requires careful management and also relies on the backup’s private key not being compromised at the same time.
Thus the disadvantage of the static pinning methods described in the previous section is that they fix the set of valid pins into the app itself as part of its configuration. This means that for an app to get a new set of pins a new version of the app must be released and be installed on a user’s device. In reality this can take many days, or even weeks, for most of the apps to update and there may always be a stubborn cohort whose apps are never updated. These would be denied access once the pins are changed, and may end up as either permanently lost users or ones which increase user support load.
Static pinning causes problems both with the speed of incident response and user retention. Ideally what is required is a means to transmit the updated pins over-the-air immediately to invocations of the app without any need for an app update. Moreover, this needs to be done in a secure manner to prevent an attacker using this as a back door to inject their own pins to undermine the pinning protection. Approov offers a dynamic pinning solution that fulfils these requirements and is described in the next section.
When the Approov SDK connects to the Approov cloud the set of public key pins for API domains being protected are downloaded automatically. These will then be stored in the dynamic configuration and thus be available immediately the next time the app starts up. This is convenient for apps developed using frameworks that require any public key pins to be presented very early during the initialisation of the app.
The overall architecture is as follows:
The SDK configuration is signed using Elliptic Curve Cryptography (ECC), with the public key held in the initial SDK configuration and the private key held securely in Approov’s servers. This allows over-the-air dynamic updates to the configuration to be sent to running apps that can be verified as being untampered and authentically issued by the Approov servers. This is guaranteed even if the update is transmitted over a channel already compromised by a MitM attack. An attacker cannot know the private key to tamper with an update. They could block an update of course, but only while they have control of all communication, so any other use of the app will cause an update to be received.
If any change is made to the API domains and their pins using the approov
commands described here, then an updated dynamic configuration will be transmitted to all any app that requests a new Approov token. The dynamic configuration is signed with the ECC private key, preventing any possibility of tampering and proving that the update has been issued by the Approov servers. This updated dynamic configuration will be written to the local storage of the app, and will either have an impact next time the app is started or immediately, depending upon the implementation of the pinning in the app.
This dynamic pinning is primarily designed to protect the channels over which Approov tokens are being transmitted. However, there is no reason why it can’t be used to protect any domain that the app communicates with, even if no Approov tokens are transmitted. The only restriction is that there must be sufficient knowledge about how certificates will be presented on that endpoint so that there is notice if there is an upcoming change that may impact the public key.
The dynamic configuration update can also be used to perform over-the-air updates of the networking access rules (in terms of domain names and timeouts) that are used by the Approov SDK to access the Approov cloud servers.
Any domain can have a list of pins expressed as subject public key info hashes in base64. A pin is considered to have passed if any part of the certificate chain includes one of the pins provided. Thus intermediate public key pins are supported. Backup pin(s) can also be included if desired.
Facilities are provided for extracting the public keys of certificates directly, or live from existing endpoints. Certificate extraction allows pins to known prior to certificates going into service, so that the pins can be added to the Approov server configuration ahead of time as additional pins. Ideally this should happen some time before the new certificate is about to go live. This ensures that there is absolutely no interruption of service to the app since it will have already received the configuration update prior to the certificate changing.
If the app is able to receive a new configuration update and immediately change the pins being used, then the update only needs to occur early enough that any prior Approov token fetch will have received the configuration revision. This only needs to be 5 minutes. In other cases, where the app cannot immediately update its pins on receipt of a new configuration, then a longer time period is required. This will depend on the circumstances in the app but it is generally advisable to push out a new configuration some days ahead of the actual change.
Note, once a change has been completed, any redundant pins can be removed in a new configuration. If they were rotated due to a breach then this is especially important, since we no longer want the app to trust the pin.
When a new API domain is added to the set of APIs, a pin for accessing that domain is automatically added. This is the pin extracted from the leaf certificate presented by the domain’s server:
$ approov api -add mydomain.com
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain mydomain.com with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
We strongly advise you to implement pinning for the API domains you access in your app. This means that any communication with this particular domain will be pinned to the value shown. In some cases it might not be appropriate to pin to the leaf certificate value, perhaps because different certificates may be presented by different servers on the endpoint. You must verify the stability of the provided pin with the backend API implementation team before going into production. If the pin changes, then your app will no longer work until a new set of pins can be pushed out, using the dynamic configuration update mechanism.
It is possible to change the pin type so that no pin is added at all with the new API domain:
$ approov api -add mydomain.com -pinType none
WARNING: adding the API will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain mydomain.com with type:account, alg:HS256, unpinned
When a leaf pin is being collected, it is obtained from both a local network request and via a network request from the Approov cloud servers. The pin obtained must match, or else an error is generated, for example:
$ approov api -add mydomain.com
domain mydomain.com local pin 1O0wDRM/roe6UTctDVQ5aN/ASNYsGQFVzXYhO34t5GE= differs from remote pin OfxtkIyzmWFXb028KrIr3VqP0XHW41mbunxtNnS3eb4= (may be due to local firewall)
This means that the observations differ. This might indicate that the leaf certificate obtained is not deterministic, or may depend on the geographic location from where the request is made. Alternatively, it might just mean that the local network is subject to interception via a firewall (this can be checked with the approov pin -api <domain> -getCertChainPins
command which obtains the full certificate chain obtained from a local request).
If you are confident that either the local
or remote
public key pin is the one required then the appropriate one can be selected using the -pinType
option, e.g.
$ approov api -add mydomain.com -pinType remote
added API domain mydomain.com with type:account, alg:HS256, pin:OfxtkIyzmWFXb028KrIr3VqP0XHW41mbunxtNnS3eb4=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
This selects the remote public key pin. Note that of course this means that the mobile app will not be able to run on the local network.
When you add new APIs, or change the pins on existing APIs, a new pinning configuration is automatically sent to your running apps when they connect to the Approov servers. If you used an extended initial configuration you may see a init-config-outdated
metric associated with your apps. You should always strive to update the extended initial configuration for your app each time you release it so that it has the correct information from launch. This is especially important if you are working with a framework that applies the pins on app launch only. Updating the initial configuration optimizes the pinning process and means there is no dependency on an update from the Approov server.
Once a particular domain has been added it is then possible to add or remove pins on that domain. An individual domain may have up to 10 different pins specified for it. A connection attempt for domain is considered to be successful if any of the specified pins is present at any level in the certificate chain obtained from the API domain server. Thus leaf and intermediate certificate pins may be specified, along with backup pins which may not yet be in service. Note that if no pins are specified for a domain then this means it is unpinned and any certificate (presuming it meets the normal criteria) is deemed acceptable.
You can add a pin to a domain with the following command. The pin value itself may have been extracted using one of the methods discussed in the following sections. For example:
$ approov pin -api mydomain.com -add Q62DnTGJsy1h+ude8HB5ZjKy0Vhg2pvTzjplWSD3hkk=
WARNING: updating the pins will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
pin Q62DnTGJsy1h+ude8HB5ZjKy0Vhg2pvTzjplWSD3hkk= added to API domain mydomain.com
Note that you need an admin role to modify the pins. You can see the pins set for any domain as follows:
$ approov pin -api mydomain.com -list
Q62DnTGJsy1h+ude8HB5ZjKy0Vhg2pvTzjplWSD3hkk=
Finally, specific pins can be removed as follows:
$ approov pin -api mydomain.com -remove Q62DnTGJsy1h+ude8HB5ZjKy0Vhg2pvTzjplWSD3hkk=
WARNING: updating the pins will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
removed pin Q62DnTGJsy1h+ude8HB5ZjKy0Vhg2pvTzjplWSD3hkk= from API domain mydomain.com
Any attempt to change the pins for the demo endpoint shapes.approov.io
result in an error.
The public key pin for a leaf certificate presented on a particular domain may be obtained as follows:
$ approov pin -api shapes.approov.io -getLeafPin matching
matching leaf pin for domain shapes.approov.io:443 is JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
This obtains the same pin that would be obtained by the -add
option described above. The possible values for the parameter are as follows:
Note that if the endpoint being accessed is not on the standard port of 443, the -port
option can be used. Here is an example that gets the leaf pin as observed by the local machine on port 8000:
$ approov pin -api mydomain.com -getLeafPin local -port 8000
local leaf pin for domain mydomain.com:8000 is 0vNoWx4vRXJCQggeFuEx6/5yyhXVl/VScIN18PmL7co=
Only a single set of pins can be associated with a particular API domain, independently of the port that is being used. If the application uses multiple different ports on the same API domain, associated with different pins, then the overall domain only needs to be added once and the set of pins for it should be the union of all those required on the different ports.
In some cases you might not want to pin against a leaf certificate but, instead, choose to pin against an intermediate certificate within the whole chain presented on a domain. A command is provided to extract this information:
$ approov pin -api shapes.approov.io -getCertChainPins
pin JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=, expiry 2020-05-22, CN=shapes.approov.io
pin JSMzqOOrtyOT1kmau6zKhgT676hGgczD5VMdRMyJZFA=, expiry 2025-10-19, CN=Amazon,OU=Server CA 1B,O=Amazon,C=US
pin ++MBgDH5WGvL9Bcn5Be30cRcL0f5O+NyoXuWtQdX1aI=, expiry 2037-12-31, CN=Amazon Root CA 1,O=Amazon,C=US
pin KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=, expiry 2034-06-28, CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US
This shows the full chain of certificates. In this example we use our demo endpoint shapes.approov.io
that is hosted with AWS and uses an AWS issued certificate. You can see the chain right up to Amazon’s root certificate. Notice that certificates higher in the chain tend to have longer expiry times. Depending upon your particular backend setup it may be better to pin to one of these certificates rather than the leaf, which is more subject to change. Of course you need to ensure that any certificate you pin to cannot itself be used to sign certificates that an attacker might use. So, in general, try and pin as low in the overall chain as is practicable.
Note that if the endpoint being accessed is not on the standard port of 443, the -port
option can be used to obtain the certificate chain on the specified port.
Note that the certificate chain is determined by making a local request. If the local network has a firewall that intercepts TLS traffic, then the resulting chain will not be the same as one observed by a device outside that network. Also be aware that, depending upon the backend API architecture, the observed chain may be influenced by the specific server that responds to the request. For example, different geographical regions may supply different certificates.
As well as getting the public key pin from a live endpoint, it is also possible to get a pin from a certificate file. This is useful when you want to add a pin to a certificate that is not yet in service. Use the following to get the pin:
$ approov pin -getCertPin mycert.crt
pin jZetC3373f9dmwxg5YE9TCZzl4MYvp0eYTiEsbcTU34=, expiry 2020-05-10, CN=mydomain.com
This provides the public key pin along with the expiry time and subject of the certificate. Note that, to use this option it is necessary to have a PEM encoded certificate file. This is normally associated with a .cer
, .crt
or .pem
file extension.
It is possible to obtain the set of pins seen by the SDK in a particular device using the Managing Devices capabilities.
Firstly you need to set the device ID from which you wish to capture the certificate information. To obtain the ID see Extracting the Device ID. A probe URL is specified that must include https
and may optionally include a port number as shown:
$ approov device -add qZka0yfv+ExvOq3PRh6pGw== -probeURL https://shapes.approov.io:443
successfully set device qZka0yfv+ExvOq3PRh6pGw==
The next time the device fetches an Approov token it will also probe the specified URL and obtain the information about the certificate chain. Remember that the device must obtain a new Approov token to do this, so if it has already obtained one then it can be up to 5 minutes before it expires.
The device information can then be obtained as follows, and this should include the probe result information (other information has been elided here):
$ approov device -getInfo qZka0yfv+ExvOq3PRh6pGw==
captured: 2020-07-23 09:54:46 BST
...
probe-result:
probe result for https://shapes.approov.io:443:
dqP5GOntRqZST5GfBj8NGpr1GMUTeKZG5g6+Q5mZFYM=: CN=shapes.demo.approov.io
YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=: CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US
Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=: CN=DST Root CA X3,O=Digital Signature Trust Co.
This is the full set of certificate pins in the chain and the subject information of the certificate. The individual pins can be added via Managing Domain Pins.
This option of extracting the pins live from a running device is useful for pin debugging purposes, or network topologies where it is not practical to run the approov
command line tool on the same network as the device and the network is not accessible via remote pin probing from the Approov cloud services. This might be the case if you are deploying Approov in a private network environment.
When pins are updated the latest versions are transmitted to all apps the next time they fetch an Approov token. To indicate a new configuration is available the isConfigChanged
flag is set, as discussed in Reacting to Configuration Changes.
However, even if an app has received the latest configuration this does not necessarily mean that it is running with the latest pins applied. This is because some app frameworks only allow the pins to be set on the initial startup of the app. If an app has been running for some time then the pins it is using may be out of date. Normally updates do not force the app to restart to get the latest pins, as this would be detrimental to the user experience in most cases where an immediate update is not required.
A mechanism is provided to signal the need to force an immediate update where this is necessary. This is discussed in Reacting to Force Apply Pins. A command can be issued to force the current version of the APIs and their pins to be forced is as follows:
$ approov pin -forceApplyPins
WARNING: forcing pin application will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
pin update has been forced
Any running app that has not read the latest pins with getPins
receives a set isForceApplyPins
flag next time it fetches a token. In the typical use case, new pins should be updated a couple of weeks before they go into service and then sometime later they can be forced. This procedure only results in a restart in the minority of apps that have not been relaunched for that period. In an emergency the forcing can be issued immediately. Note that this behaviour only impacts app integrations that are not able to dynamically update their pins.
An option is provided that checks that all of the specified API domains are accessible and that, if pins are provided, then they do actually match a certificate being presented for the domain. This can be used as a quick check that the API configuration that is currently set is valid. It is invoked as follows:
$ approov api -check
mydomain.com:
no pins defined
shapes.approov.io:
certificate chain:
dqP5GOntRqZST5GfBj8NGpr1GMUTeKZG5g6+Q5mZFYM= CN=shapes.demo.approov.io
jQJTbIh0grw0/1TkHSumWb+Fs0Ggogr621gT3PvPKG0= CN=R3,O=Let's Encrypt,C=US
pins:
dqP5GOntRqZST5GfBj8NGpr1GMUTeKZG5g6+Q5mZFYM=
matched pin dqP5GOntRqZST5GfBj8NGpr1GMUTeKZG5g6+Q5mZFYM=
The current API configuration is obtained from the Approov cloud service, and then each of the domains is checked from your local machine. If monitoring is enabled for the API domain then any port specified for that is used for the access. If the API domain is not accessible then that is considered an error.
If pins are defined for a particular API domain then the certificate chain observed is shown, along with the list of pins that are defined for the domain. If there is a matching pin then that is also listed.
The command has a return code of 0 if it succeeded or 1 if it failed. Note that if the user role is invalid, or the Approov cloud service cannot be contacted, then this is not considered a failure since the explicit purpose of the command is to check the API endpoints themselves.
The complete set of API domains and the pins that have been added to the account can be extracted into a JSON format file as follows:
$ approov api -getAll apis.json
API domains and pins written to JSON file apis.json
This command gets all of the API attribute and pin information and writes it to the apis.json
file. In this example the content is as follows:
$ less apis.json
{
"mydomain.com": {
"secretType": "account",
"algorithm": "HS256",
"pins": [],
"monitorPort": 443
},
"shapes.approov.io": {
"secretType": "restricted",
"algorithm": "HS256",
"pins": [
"JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU="
]
}
}
This shows that Approov tokens can be issued either for mydomain.com
or shapes.approov.io
.
The attributes for each domain in the format are as follows:
secretType
: The type of the API domain, primarily defining where the signing or encrypting secret key is obtained. Options are account
, keyset
, restricted
or pinOnly
.allowWeb
: This is an optional boolean flag that is only shown if true
. It indicates that the API domain may have web protection Approov tokens fetched for it.algorithm
: Provides the type of signing or encryption algorithm for the API domain (or empty for pinOnly
).kid
: This is optional and provides any key ID for tokens issued for the domain. This is always present for the keyset
type.pins
: The list of valid pins for the domain. Empty indicates that no pinning is applied.monitorPort
: If contiuous monitoring is enabled for the domain then this specifies the port being used (normally 443).API configuration may be set in the same format as that obtained with the API -getAll
option. New domains may be added, removed or edited. For instance, we may edit the JSON received in the previous section with a different domain and two different potential pins.
$ less apis.json
{
"another.domain.com": {
"secretType": "account",
"algorithm": "HS256",
"pins": [
"/ABoW73P1I3gWumpugB6zzHtqjl0+yFKRBEvJW8I3sQ=",
"Wp/PdTn4cw7/jwsS9+DnheEm5AbtTEsmVPAGOGY3tHk="
]
},
"shapes.approov.io": {
"secretType": "restricted",
"algorithm": "HS256",
"pins": [
"JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU="
]
}
}
We can then update the Approov server configuration as follows:
$ approov api -setAll apis.json
another.domain.com: matched pin Wp/PdTn4cw7/jwsS9+DnheEm5AbtTEsmVPAGOGY3tHk=
shapes.approov.io: matched pin JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
WARNING: updating the APIs will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
APIs set successfully
An error is generated if the input is not well formed JSON. Each of the domains provided are checked to see if they are reachable and if any one of the pins provided match. This ensures that the configuration is valid prior to updating it in the Approov cloud and distributing it to apps. If there is a problem then the follow message is seen:
WARNING: problem detected with APIs, we do not recommend you proceed (unless caused by known local firewall issue)
You can still choose to proceed if the issue may be caused by your local connectivity, since the check is made via the local network, but you should be convinced that this is the only issue or else it would be unwise to proceed.
We do not generally recommend that you use this option. It is better to perform finer grain management using the -add
and -remove
options for individual APIs and pins.
An admin role is required and further confirmation is expected. This is because these changes will have an impact on production within 30 seconds and as such are dangerous if domains have been accidentally removed or if pins are incorrect.
Any attempt to change the pins for the demo endpoint shapes.approov.io
cause an error.
The previous section described how the configuration can be managed to make use of the Approov dynamic pinning feature. This section provides examples of how the pins may be set using different languages and HTTP stacks.
This section provides a high level overview of pinning implementation and its testing. For details of the implementation for a particular http
stack you should consult our Quickstart Frontend Integrations. Pinning is performed automatically as part of these integrations and we strongly recommend that you use these where possible.
Pins can be set immediately after the Approov SDK has been initialized and any dynamic SDK configuration has been written. At this stage some pins will be available, either through the initial SDK configuration or through dynamic SDK configuration updates that have been received since the app was first installed.
This early access to the pins allows the pinning to be set up even for app development frameworks that require the information very early during the app startup. However, for these pins to actually have any impact in the app it is necessary to add code to communicate the pins to the TLS stack.
The SDK provides a getPins
method that can be used by the app to obtain the currently configured set of pins to be used. The pins will be set up immediately after the initialization of the Approov SDK. The getPins
method should be called with the parameter public-key-sha256
, indicating the pinning type being performed. This specifies that the pinning type is the SHA256 hash of the Subject Public Key Information (SPKI) of the certificate. This is currenly the only type of pinning supported.
Whenever an Approov token is fetched, a check should be made to see if a new dynamic SDK configuration has been received. This will happen for apps when a Pinning Configuration Change has been made in the account. The following code snippet shows how to perform this check and update, assuming that the approovResults
variable holds the result of a token fetch:
if (approovResults.isConfigChanged()) {
saveApproovConfigUpdate()
}
if approovResults.isConfigChanged {
saveApproovConfigUpdate()
}
If a configuration update is received then this calls the saveApproovConfigUpdate
method as defined here, or some equivalent for your app. This causes the new dynamic SDK configuration to be written to persistent storage so that it will be available before any network operations the next time the app is started.
A change to the SDK configuration is typically the result of some changes to the API domains and/or the pins that are set on them. Ideally, the app should react to those changes immediately by calling code to reset the pins associated with the APIs used by the app. The getPins
method always returns the latest set of pins communicated to the app.
Whether immediate reactivity is possible, or architecturally easy to achieve, will depend on your app and the framework in which it is developed. Resetting pins typically requires a new pin configuration to be provided while constructing a new instance of the HTTP client used for making API calls. We recommend this as the best approach where possible, since it makes your app very reactive to any pin changes. In other cases, the app may only need to read the pinning information when actually making the TLS connection. In this case the app will always be reactive to the pin changes.
In some cases it is not possible for the app to immediately react to pinning changes. In this case the app may need a restart to apply the new pin configuration, or prompt the user to restart the app. Generally though we do not wish to restart the app every time there is a configuration change, as this could result in a poor user experience. Moreover, we may wish to push new pins to the configuration ahead of time so that they are more likely to be available when needed and thus avoid the need for an invasive restart altogether, since there is a higher chance of the app being restarted anyway.
One approach is to postpone any restart until there has been both a pending configuration update and a pinning failure exception has been caught by the app. That way a restart only occurs when absolutely necessary. Of course this may complicate the code by requiring special checks and logic on pinning failures.
Another mechanism is provided that is initiated by the Forcing Pin Application command. A flag is returned in the token fetch status:
if (approovResults.isForceApplyPins()) {
...
}
if approovResults.isForceApplyPins {
...
}
If this flag is set then it indicates that the last call to getPins
made by the app obtained a version of the pins that is older than that which is being forced. Thus if an app has been naturally restarted since the last pin configuration update then it will already have a sufficiently recent version and the flag is not asserted,
The fact that these pins have been forced indicates that it is important that the new version is being used, perhaps because a certificate has been compromised or new pins are required to access an endpoint. If the flag is set then the app should take action to ensure that the pins are updated to the latest version, even if this means initiating an app restart. Calling getPins
will reset the flag for the next time an Approov token fetch is made.
It is important that any pinning implementation is tested, by forcing the app into situations where the pins are incorrect with respect to the certificates being presented on the API endpoints. It is necessary to show that this is handled correctly and, importantly, that dynamic configuration updates issued from the Approov cloud update the app’s pins correctly.
In production it would be undesirable to change the certificates on the real API endpoints to do the testing as this would impact all app users. To avoid the need to do this, Approov provides facilities to modify the set of pins provided to a particular device being tested. These facilities are discussed in detail in the Managing Devices section. The idea is that for a particular device the pins can be set to a known bad value that will trigger the pinning exception so that it can be tested, i.e. Approov changes what the app is pinning against rather than what is being presented by the API endpoint. This is an equivalently good test.
The following is a recipe of commands to perform a test on your pinning implementation. Firstly you will need to get the ID of your device, see Extracting the Device ID. You should then check that the app is operating normally and that you have pins defined for the endpoints you are trying to protect with pinning. You can check what pins are set using Getting Pin Configuration.
Next, you can block all of the communication by forcing bad pins to be downloaded as a dynamic configuration for your app. For example:
approov device -add h4gubfCFzJu81j/U2BJsdg== -pinMode block
Note that nothing will happen until the next Approov token fetch is made. An existing token may have a lifetime of up to 5 minutes. A new dynamic configuration should then be received. You may be able to see this in the app’s logging. How quickly your app can respond to this update will depend upon the app’s architecture. Once the update has been actioned, you should see an appropriate error from your app when it attempts to make API calls. We suggest you go through this whole process without exiting the app.
You can then simulate what would happen to your running app if a new set of valid pins are transmitted to your app via a dynamic configuration update:
approov device -add h4gubfCFzJu81j/U2BJsdg== -pinMode pin
This sets the pins back to the default for the account. Once any currently fetched Approov token has expired (up to 5 minutes) a dynamic configuration update is transmitted to the app. Depending on the pinning implementation, this might have an immediate impact or after some time if the pins can only be rebuilt then. If your app’s pinning implementation cannot rebuild the pins until the app is restarted then it should provide a user message to that effect.
It is vitally important that you test that your app is able to recover from having incorrect pins to correct ones via a dynamic configuration update. This is the process that your app must successfully follow should there be a need to supply new pins to your app over-the-air.
It is also possible to block all app communication, including that between the Approov SDK and the Approov cloud as follows:
approov device -add h4gubfCFzJu81j/U2BJsdg== -pinMode blockAll
In this case you will receive a MITM_DETECTED
error from token fetch attempts. This will allow you to check the handling of this in your app.
Pinning relies on the underlying http
stack implementation, that is part of the device OS, to implement it. If this underlying implementation is compromised then it is possible that the pinning is not actually restricting TLS connections in the way intended, and potentially allowing an attacker to MITM connections. Approov incorporates various detections of modifications to the runtime environment that may allow such a compromise to occur. In such cases the device will typically not be issued with valid Approov tokens. However, if a particularly lax rejection policy has been selected then there is a danger that such underlying tampering could be occurring whilst valid Approov tokens are issued.
Thus an additional pinning verification mechanism is provided. This allows an arbitrary URL to be specified. The SDK attempts to connect to this the very first time a new app installation tries to fetch an Approov token. The fetch uses the underlying pinning mechanisms of the device OS and specifies a pin that will not be present. Thus the connection attempt should fail. If it succeeds then it indicates that the pinning mechanism has been compromised in some way. If this happens then the app installation will never be issued valid Approov tokens.
Set the URL to be used as follows:
$ approov policy -setPinningTestURL https://mydomain.com:443
WARNING: updating the pinning test URL will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
pinning test URL was updated successfully
The URL should be specified as a full URL with https
and may optionally include a port number. This URL should be something that it is possible to perform a successfully http GET
operation upon and obtain a 200 OK response. The URL should also not result in a redirect. If the URL is not valid then approov
tool will issue a warning. The idea is that if SDK is able to perform this GET
, despite the pinning restriction, then this is the trigger to mark the app as being compromised.
We recommend that the URL is one of the key API backend domains that your app uses and is protected using Approov.
You will be able to see devices that have compromised using the pinning-tampered
device property flag.
You can see any URL that has been set as follows:
$ approov -getPinningTestURL
https://mydomain.com:443
Finally you can remove the URL at any point using:
$ approov policy -removePinningTestURL
WARNING: updating the pinning test URL will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
pinning test URL was updated successfully
This feature is only supported with SDKs that are version 2.4.0 or later. Earlier version SDKs will not perform the check.
A security policy is set at the account level and determines which apps may be issued with a valid Approov token.
Approov provides fundamental checks regarding the integrity of the app itself, so that valid Approov tokens are only issued to valid app instances. Additionally, various other runtime integrity checks are also performed and the results transmitted to the Approov cloud server in a secure manner. Whether these checks should result in an invalid Approov token or not is determined by the security policies that are selected. This gives you the flexibility to permit accesses or block them according to your risk assessments and the characteristics of your user base. For instance, if you develop a banking app, you may take the view that no rooted device should receive a valid Approov token. However, other consumer apps, deployed to certain markets or demographics, may require rooted devices to be accepted. Thus the security stance must be informed by the characteristics of the app’s user base and the particular threats that Approov is being used to defend against.
A security policy is shown and specified as three comma separated individual selections as follows:
default
.When a new account is created it is allocated a security policy of default,default,default
. This provides a restricted policy of only issuing valid Approov tokens where no threat has been detected.
Note that security policies may also be set for individual devices as well as the whole account as described in Managing Devices. For this reason blanket policies such as whitelist and blacklist are provided that would not normally be applied to the whole account.
It is possible to determine the current security policy for an account using the following command:
$ approov policy -get
security policy is "default,allow-root,all" meaning:
rules: default (Default security rules)
rejection: allow-root (Accept rooted Android devices but reject emulators/simulators and Frida/Cydia/Cycript)
annotation: all (All flags in Approov token)
flag level rejection policy:
reject app-not-registered App is not registered, which may indicate that it is fake or tampered
...
This provides information about the security policy selected, including a description of the effect of each component of the policy.
It also outputs a full list of the device property flags that are determined for each device, with a short description for each. This list is subject to dynamic change as new detections are added to Approov. A reject
tag is added to the output next to any flag that causes a rejection (the return of an invalid token from an Approov token request). Thus you can see the impact of the chosen rejection policy at a fine grain level.
If you have overridden the preset settings for the individual flag rejection status, then the rejection policy is shown as custom
.
The security policy may be changed for the account using the following command:
$ approov policy -set default,allow-root-and-jailbroken,xposed
WARNING: updating the security policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
This requires an admin role. This particular command will change the security policy to allow jailbroken devices as well and to only show the xposed
flag as an annotation. The user must confirm the confirmation prompt in order to apply the new security policy.
If adding the new security policy is confirmed then it will have an effect on the account within 30 seconds. Care must be taken when making changes like this to a live account in case a new policy has a detrimental impact on legitimate users of your app.
The rejection policy is the second parameter in the comma separated list for setting a security policy. A number of preset options are available that are used to determine the set of characteristics that are used to reject a particular Approov token request (resulting in an invalid Approov token). It is also possible to have custom rejection policies. The options are as follows:
Policy | Description |
default
|
Reject all rooted/jailbroken devices, emulators, simulators, frameworks and cloned multiapps. |
strict
|
Reject all rooted/jailbroken devices, emulators, simulators, frameworks and cloned multiapps, and strictly require SafetyNet/DeviceCheck if enabled. Note that this causes any devices not capable of running DeviceCheck/SafetyNet to be rejected. Note that if you enable DeviceCheck/SafetyNet with this policy selected then any app that has already launched and fetched an Approov Token prior to that will be rejected. Thus we suggest you only enable this policy some time after setting up DeviceCheck or SafetyNet if you have an existing installed base of running apps. |
allow-cloned
|
Reject all rooted/jailbroken devices, emulators, simulators and frameworks. However, cloned apps running in multiapps are allowed. |
allow-xposed
|
Allow Android Xposed, but otherwise reject all rooted/jailbroken devices and emulators, simultors, other frameworks and cloned multiapps. This option is provided because for some markets Xposed is a popular modding framework for devices, without an intent to attack a given app. If an Android device is rooted without using Xposed, or if an active Xposed module is implicated in data stealing, then a rejection is made. |
allow-root
|
Accept rooted Android devices but reject emulators/simulators, cloned multiapps and Cydia/Cycript/Frida. Detected jailbroken iOS devices are rejected. |
allow-root-and-jailbroken
|
Accept rooted Android, iOS jailbroken devices, emulator/simulators, cloned multiapps but reject Cydia/Cycript/Frida. |
allow-root-and-jailbroken-only
|
Accept rooted Android and iOS jailbroken devices, but reject emulator/simulators/Cydia/Cycript/Frida/cloned multiapps. |
allow-raj-only-short-lifetime
|
Accept rooted Android and iOS jailbroken devices, but reject emulator/simulators/Cydia/Cycript/Frida/cloned multiapps. Moreover the Approov tokens issued have a short lifetime of only 45 seconds (with an additional 15 second grace period). |
whitelist
|
A highly permissive policy that generates valid Approov tokens except when there is direct evidence of tampering in the Approov SDK. This generates valid tokens even if the app is not registered. This will typically only ever be applied to individual devices. Note this also has the side effect of disabling any defences that might cause an app to crash if it is being debugged. The app may need to be restarted to remove any already applied protection. |
blacklist
|
Always rejects, thus generating invalid Approov tokens. Typically this will only ever be applied to individual devices. |
In addition to the preset rejection policies, it is also possible to use a customized rejection policy. The most applicable rejection policy should be used as the starting point, then it is possible to add or remove flags that will cause a rejection. If these modifications are made then the rejection policy will be automatically changed to custom
. You can set a new security policy at any time, which will discard your custom policy and replace it with the preset policy you select.
Firstly get the current policy to obtain a list of all of the flags, and their current reject status. To add a property to the set that cause a rejection whenever they are detected, you can run a command similar to the following:
$ approov policy -addRejectFlag automated
WARNING: updating the security policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
This causes the flag automated
to be added to the set that will cause a rejection. You can see the status by getting the security policy. Note you need an admin role to perform this operation.
You can also remove a property from the rejection set by using a command similar to the following:
$ approov policy -removeRejectFlag automated
WARNING: updating the security policy will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
security policy was set successfully
Within 30 seconds the customized policy will be applied to new Approov token fetches. Note that, if the app is already running then a new token fetch will be necessary before the new policy is enforced (up to 5 minutes).
It is possible to use any preset rejection policy as the starting point for changes. However, we do not recommend starting with the special policies of whitelist
or blacklist
as these have other, hidden, attributes.
The annotation policy is the third parameter in the comma separated list for setting a security policy. This determines what strings any anno
claim in the Approov token may have. These are a subset of the device property flags. This allows some ancilliary information to be present within the token. This is particularly useful where a valid Approov token is still being issued, but analysis has demonstrated some questionable aspects of the requesting device which may need to be analyzed by the backend API in conjunction with other information sources.
The following are valid options:
Policy | Description |
default
|
No annotations are made. |
info
|
This provides the properties risky-device , xposed , automated , automated-launch and non-standard-launch in the annotation. This is useful for putting in place other backend authorization mechanisms to determine if traffic should be allowed depending on the flags present and other factors.
|
risk
|
This provides the property risky-device in the annotation if the device has a property that makes it high risk. This is useful for putting in place other backend analysis that might limit the operations that risky devices can perform.
|
all
|
Indicates that all available public properties should be included in the Approov token. This annotation policy should never be enabled for the whole account, only for specific devices, since this information may provide an attacker with significant information about the reasons for any Approov rejection. If constant monitoring of these properties is required in the backend API then we suggest that JWE Approov tokens are used so that the content is encrypted. Note that an alternative mechansim is provided using Attestation Response Codes and we recommend that this is used instead unless the results of the property checks are to be used as part of the request processing. |
It is possible to associate a key ID with all JWS tokens created by Approov. You may arbitrarily choose the key ID as follows:
$ approov secret -setKeyID mykeyid
Once this is set then all issued JWS tokens will contain a kid
field in the header with the value provided. The currently set value can be obtained with:
$ approov secret -getKeyID
Inclusion of a key ID can be removed with:
$ approov secret -clearKeyID
The addition of a key ID does not impact tokens issued for certain Approov managed domains, such as shapes.approov.io
nor for domains using encrypted JWE tokens.
Approov provides facilities to apply specific policies to particular devices that are being used. This affords flexibility in work practices during development, in continuous build systems, and to allow pentesting and analysis of an Approov protected app.
In order to modify the policy of a particular device, its ID must be extracted. There are several methods by which this extraction may be done.
Via the logging that is output by Approov when the SDK is first initialized. On Android this can be seen by looking in logcat
under the Approov tag. For instance, the following is logged when starting a demo app. The base64 string h4gubfCFzJu81j/U2BJsdg==
at the end of the line is the device ID. It represents a 128-bit ID for the app and device combination. On iOS this logging is available in the documents folder for the app in the file Approov.log
.
2019-05-20 13:56:57.789 29546-29546/com.criticalblue.demo I/Approov: test-account, com.criticalblue.demo, 2.0.4(1021), h4gubfCFzJu81j/U2BJsdg==
Via extraction from an Approov token that has been generated on the device. A loggable token contains the device ID directly in the did
claim. It can be extracted from an ordinary Approov token using the command line tool, for example:
$ approov token -check eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaWQiO…
failed: {"did":"h4gubfCFzJu81j/U2BJsdg==","exp":1558626254,"ip":"85.233.105.183"}
By calling the getDeviceID()
method on the SDK interface. This provides the base64
encoded string of the device which may be presented in some way within the app.
By looking at the devices that have recently requested an Approov token using approov device -getFetches timed
. Note that if you are adding a custom device you can use the shorthand approov device -add latest
to select any device that has made a request in the last two minutes, as long as it is the only device that has done so.
By adding a device filter, perhaps using your IP address, and then analyzing filtered devices. From this you will be able to see the specific device ID that has been filtered.
To respect an individual’s right to privacy, both Apple and Google limit the ability to uniquely identify a device. The identifiers that are truly fixed for a device are hidden and device IDs are generated on-the-fly for new app installations. These, on-the-fly, platform generated device IDs are consistent for the same app but vary across iOS and Android as described below.
Approov device IDs are partly derived from the platform provided device ID, but also include information derived from the app identifier, so device IDs are always per-app device IDs. Thus if you are developing two different apps on the same device you will find that they will be allocated different device IDs. Moreover, any change to the package name of the app will also change the device ID.
Thus you must be aware that the ID for a particular device may change, and this will cause the applied policy for that physical device to be lost. The new device ID must be extracted and the device added again, with the old redundant one removed.
The underlying identifiers used to derive the Approov device ID are as follows:
An individual device may be assigned a specific security policy as follows:
approov device -add h4gubfCFzJu81j/U2BJsdg== -policy default,whitelist,all
This causes the given device to have the particular policy applied without applying it to the rest of the account. This change should be actioned for new Approov token requests within 30 seconds. Remember though that if your app is already running on the device and has fetched a token then it might not need to fetch a new one for up to 5 minutes, in which case the revised policy will only be apparent when that new fetch occurs.
The example rejection policy of whitelist
is a common one for development. By whitelisting the device, it is possible to always get a valid Approov token to access endpoints without needing to re-register modified apps. This policy also provides valid Approov tokens when debugging the app.
If the device is the only one that has been used recently on your account you can use the latest
shortcut, rather than having to specify the device ID explicitly.
approov device -add latest -policy default,whitelist,all
Although, you should note that, the device must have fetched an Approov token in the last two minutes, and must be the only device doing so in that time. This option cannot be used reliably once you have your app in production where there may be a large number of end user Approov token fetches in the 2 minute period.
There is a maximum of 25 devices that may be added to an account at one time.
When adding a device it is also possible to set a label for it to make it easier to remember what device it is:
approov device -add h4gubfCFzJu81j/U2BJsdg== -policy default,blacklist,default -label bobs-iPhone7
This label will then be shown when you list the devices. Note that if you wish to include spaces in the label then you must surround it with quotes. This example uses the rejection policy blacklist
to ensure the device never receives a valid Approov token. This may be useful for testing.
It is also possible to set the pinning mode for a particular device. This changes the way pins are handled for the device and can cause a new dynamic configuration to be downloaded to the app. These options are covered in more detail in Testing the Pinning Implementation. This command removes all pinning for the app, by causing an empty set of pins to be transmitted as the dynamic configuration:
approov device -add h4gubfCFzJu81j/U2BJsdg== -pinMode unpin
This has the further impact of sending a special dynamic configuration update to the app on the particular device that has all the certificate public key pins removed. This causes all pinning protection to be removed from the app, allowing a proxy (such as Charles Proxy or MITM Proxy) to be used to intercept traffic between the app and the API endpoints. This can be useful for debug or pentesting. Note that some apps may require a relaunch to unpin after receiving the update if they are not written to be immediately reactive to pinning changes.
Other options may be provided for the pinning mode as follows:
block
: This transmits an fixed but invalid pin for in the dynamic configuration for all domains that have had pins added. This will force the app to experience a pinning failure in the same way it would if the API endpoint’s certificates had changed. This can be used for testing what happens in the app when such a certificate mismatch occurs.blockAll
: This works like block
but it also causes the Approov channel to detect a MITM. THis simulates what happens if all TLS traffic is being intercepted. In this case any Approov token fetch (that needs to fetch a new token) will receive a MITM_DETECTED
error.pin
: This is the default and returns the device to the default pinning for the account.Note that since no security policy is explicitly set, the device is assigned the current security policy for the account. You may also set an explicit security policy for the device in the same command.
A full list of devices that have had a special override security status can be obtained with:
approov device -list
This produces a list, for example:
1 device:
h4gubfCFzJu81j/U2BJsdg== default,whitelist,all unpin A N Other
It shows the device ID, the security policy currently being applied and any pinning mode that has been set (or pin
by default).
The name of the user that added the device is also provided. If a device label has been set then this is also shown. This makes it easier to determine the user of a particular device.
If the device was added by a user with the pentest
role then this is also shown as an attribute on the device. A user with the pentest
role is only able to see or modify devices that have been added using that role, so that they are not able to determine the device IDs being used for development.
A particular device can be removed as follows:
approov device -remove h4gubfCFzJu81j/U2BJsdg==
The standard account policy will be applied to the device within 30 seconds for new Approov token fetches. Note that, if the app is already running then a new token fetch will be necessary before the new policy is enforced (up to 5 minutes).
Device removal is not restricted to the user that added the device.
Sometimes it is desirable to be able to remove multiple devices with a single command. For instance, if we have:
approov device -list
3 devices:
h4gubfCFzJu81j/U2BJsdg== default,whitelist,all unpin A N Other
VI2c+UugQ6L7v0wkovXOSg== default,whitelist,all pin A N Other
iDpOemiTdame9qvAQF4w/g== default,whitelist,all pin Me
You can issue a command to remove all devices associated with the user name A N Other
as follows:
approov device -removeMatching "A N Other"
2 matched devices:
h4gubfCFzJu81j/U2BJsdg== default,whitelist,all unpin A N Other
VI2c+UugQ6L7v0wkovXOSg== default,whitelist,all pin A N Other
WARNING: are you sure you wish to remove these devices
ATTENTION: If you wish to continue then please type YES and return: YES
removed h4gubfCFzJu81j/U2BJsdg==
removed VI2c+UugQ6L7v0wkovXOSg==
You must specify the name in quotes if it contains any spaces. The matching devices will be listed and you will be asked for confirmation.
Note that the provided string also matches against any label that has been provided.
This facility allows any individual device to be banned on the basis of its device ID. You would typically do this as a result of malicious activity that you may have detected. Such a device will then be unable to receive valid Approov tokens.
The first step is to determine the device ID by obtaining an Approov token that has been issued to that device. Each Approov token contains a did
claim which holds the device ID. You can extract the claims from the token using the Checking Token Validity command. So, assuming a device ID of h4gubfCFzJu81j/U2BJsdg==
was extracted, the device can then be banned with the following command:
$ approov device -add h4gubfCFzJu81j/U2BJsdg== -banMode ban
successfully set device h4gubfCFzJu81j/U2BJsdg==
The next time this device tries to fetch an Approov token it will be added to the banned list and will not receive a valid Approov token.
If you have the all
Annotation Policy selected then tokens rejected due ban have did-ban
listed as a rejection reason.
Once the ban has been actioned there is no need to retain the device in the custom set for Approov, so you can remove it again with:
$ approov device -remove h4gubfCFzJu81j/U2BJsdg==
successfully removed device h4gubfCFzJu81j/U2BJsdg==
The ban is only related to the particular device ID. On iOS in particular, if the app containing the Approov SDK is uninstalled and reinstalled again it may be allocated a different device ID and thus evade the ban. In order to combat this the DeviceCheck Integration can be used to provide a permanent ban of a physical device, unrelated to the device ID being used.
A device ban can be removed if the current device ID can be obtained. As discussed in Banning a Device, this may be obtained from an example Approov token issued to the device. Initiate the unban as follows:
$ approov device -add h4gubfCFzJu81j/U2BJsdg== -banMode unban
successfully set device h4gubfCFzJu81j/U2BJsdg==
The next time this device tries to fetch an Approov token it’s ban status will be removed, and it will be issued with a valid Approov token (presuming it meets all of the other criteria). Once the unban is actioned, remove the device from the custom device set.
Once a given device ID has been unbanned, it cannot be banned again (unless all device state is cleared).
You can obtain a list of the persisted flags associated with a particular device ID as follows:
$ approov device -getDeviceState h4gubfCFzJu81j/U2BJsdg==
did-ban
A list of flags associated with the device ID is provided. In this case it shows that this particular device has been banned. You may see other flags associated with persisted state, especially those related to DeviceCheck and SafetyNet. The evaluation of these tokens are held as persisted state so that the costly exercise of obtaining these tokens doesn’t have to be repeated each time the app on a particular device is launched.
If the device has not been used on the Approov account then you will see the message device state has not been set
.
If a device ID has been banned, and then subsequently unbanned, you will see both did-ban
and did-unban
in the list. The did-unban
always takes priority.
It is possible to clear the persisted state of all devices in the account. This means that any previously banned devices will become usable again. Moreover, if you are using DeviceCheck Integration or SafetyNet Integration then the previous results of these token fetches will be erased and devices will be forced to perform the checks again next time they connect to the Approov service.
You can clear the state as follows:
$ approov device -clearDeviceState
WARNING: clearing device state will require all app installs to redo any DeviceCheck/SafetyNet checks (consider SafetyNet API quota limits)
WARNING: it is not advised to use this option if DeviceCheck autoban (bit availability of both) is being used
WARNING: this will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
successfully cleared all device state
You need an admin role to perform this operation and pay careful attention to the warnings. In particular, if you are using SafetyNet and you clear the device state, this means that new SafetyNet attestations will be required for all devices as they preform their first Approov token fetch after the reset and this may exceed the SaftyNet API quota you have in place.
Note that clearing the device state will have no impact on your billing or the calculation of hourly, daily or monthly active devices. However, it will impact the derivation of the new-did
flag so existing app installations will be considered to be new the next time they fetch an Approov token.
If you use this in combination with Automatic Device Banning then the count of installations on the device will be incremented. This could cause unexpected device banning.
This section covers the optional device filtering feature in Approov.
Facilities are provided to obtain detailed information about the attributes of devices. Filters may then be defined and checked against all devices attempting to fetch Approov tokens. These filters can match against specific device attributes.
When a filter matches then this is shown in a special graph within the Live Metrics. This allows an appreciation of the timing and prevalence of particular attributes to be known. This can be useful for understanding the population of devices that are using your Approov account. Moreover, these facilities may be used for understanding any suspicious behaviour and/or by CriticalBlue support staff understanding any irregularities in the operation of apps using your account.
Options are provided to ban particular devices with specific characteristics, and even to apply special security policies to matching devices. This may be used by CriticalBlue support staff to run customized security rules that are able to gather more detailed telemetry from specific devices for analysis.
It is possible to obtain more detailed device information for any device that has had a device specific security policy added. At least one Approov token fetch must be performed to collect the information (remembering that a previously fetched Approov token may take up to 5 minutes to expire before the app needs to fetch a new one, if it is not restared).
Once this is done the information can be obtained as follows, by specifying the device ID:
$ approov device -getInfo qZka0yfv+ExvOq3PRh6pGw==
captured: 2020-07-23 11:51:26 BST
rejection-flags: app-not-registered
public-flags: app-not-registered,rooted,root-risk,xposed
ip: 1.2.3.4
device-id: qZka0yfv+ExvOq3PRh6pGw==
device: Android 8.1.0, Nexus 5X, google, bullhead, en
archid: arm64-v8a
device-props: {'Model':'Nexus 5X','Fingerprint':'google/bullhead/bullhead:8.1.0/OPM6.171019.030.E1/4805388:user/release-keys'}
sdkid: 4000
sdk-version: 2.4.0
app-name: Approov OkHttp Shapes
app-version: 3.0(3)
appid: com.criticalblue.demo
app-id-sig: NhVaAEGs3U6fLObhZAIdRh71wVF7kSOL0aAMv+e5TvU=
app-sig: tL8CNNpf4XlekWkwuyJaCRtcntJyjZ1ujYJ7CZMO+Fk=
attest-reason: launch
The information most recently collected is shown, along with the local time of its capture. The information consists of a series of <key>:<value>
attributes. The exact set of information may vary over time and also between iOS and Android.
In this case the public-flags
shows the full set of device property flags that have been collected. The IP address of the client is always shown too, independently of the IP Tracking Policy that has been selected. Although, you should note that, the IP addresses are only held in the memory of the Approov cloud servers and never committed to any file storage.
In the case of Android the device-props
provides more detailed information about the particular model and OS build of a device (although it does not uniquely identify an individual device). The app-sig
is always present and shows the signature of the particular app. On Android the app-id-sig
is also present and provides the overall identity signature used when registering an App Bundles or APKs signed by Google Play signing (as the raw app signature may change depending on the app delivered to an individual device).
The individual device information is collected and held by the Approov servers on a best effort basis. The information may be cleared from time to time so you should save it in a local file if you wish to keep it more permanently.
Information for new devices (i.e. device IDs that have not be seen previously) is collected automatically. It can be obtained by using new-did
as the parameter for -getInfo
:
$ approov device -getInfo new-did
--- w4mmGFZkpz5oPz6w6gl1Cw== 2020-07-23 12:10:43 BST ---
rejection-flags: app-not-registered
public-flags: app-not-registered,jailbroken,devicecheck-unavailable
ip: 1.2.3.4
device-id: w4mmGFZkpz5oPz6w6gl1Cw==
device: iOS, 12.1.2, iPhone7,2, en_ES
archid: arm64
sdkid: 4505
sdk-version: 2.4.0
app-name: Approov Shapes
app-version: 1.0
appid: ios.swift.shapes.demo.approov.io
app-sig: DRJNqt37tsdHwb9FKFT80dPqXlOHqZnZy68zAiVSsvM=
attest-reason: expiry
A stream of information for the the most recent new devices is provided, with a separator providing the ID and the local capture time.
The new device information is collected and held by the Approov servers on a best effort basis. Only the information for the last 100 new devices is held and earlier entries are discarded. Note also that the information may be cleared from time to time so you should save it in a local file if you wish to keep it more permanently.
If you wish to obtain information for all new device IDs then the following short bash
script can be used. This appends to the approov-new-did.txt
file any new devices on a per-minute basis. This ensures that, unless there are more than 100 new devices per minute, all the new device information will be captured.
#!/bin/bash
while true; do
echo "getting new devices"
approov device -getInfo new-did -clear >> approov-new-did.txt
sleep 1m
done
The use of the -clear
option ensures that prior data is cleared on each call so there is no duplicate information in the generated file. Note that this approach cannot be used by more than one user at a time.
A device filter may created that checks the attributes of every device fetching an Approov token to determine if it has a particular attribute. For instance:
$ approov filter -add myip -key ip -matchValue 1.2.3.4
successfully set filter myip
This adds a new filter called myip
. This name is used to reference the filter and also appears in live metrics if the filter is matched. The name is restricted to a maximum of 20 characters of lower case alphanumerics and the -
sign only. If a filter name is reused then the previous settings are overridden.
The -key
option determines the particular key in the information to be checked against, ip
in this case. If the key is not present then the match never occurs. The -matchValue
specifies the specific text for the value for the match to occur. In this case the match is for any device using the IP address 1.2.3.4
. (This initial form requires an exact verbatim match, to have a single filter with more general matching semantics, see the section on Regular Expression Filtering.)
With the default options the only impact is to show the match in the live metrics.
Filtering may be restricted to only new devices by using the -newDIDOnly
flag:
$ approov filter -add special-app-sig -key app-sig -matchValue DRJNqt37tsdHwb9FKFT80dPqXlOHqZnZy68zAiVSsvM= -newDIDOnly
successfully set filter special-app-sig
In this case the matching is looking for a particular app signature for device IDs being used for the first time.
A maximum of 25 filters may be defined at any one time.
It is possible to capture full device information for any device that matches a filter. To enable this you must use the -captureDeviceInfo
flag on the added filter, for example:
$ approov filter -add myip -key ip -matchValue 1.2.3.4 -captureDeviceInfo
successfully set filter myip
Information for any device that matches the myip
filter is then collected. The informaiton can be obtained by using filtered
as the parameter for -getInfo
:
$ approov device -getInfo filtered
--- w4mmGFZkpz5oPz6w6gl1Cw== 2020-07-23 12:10:43 BST ---
matched-filters: myip
flags-all: app-not-registered,jailbroken,devicecheck-unavailable
ip: 1.2.3.4
device-id: w4mmGFZkpz5oPz6w6gl1Cw==
device: iOS, 12.1.2, iPhone7,2, en_ES
archid: arm64
sdkid: 4505
sdk-version: 2.4.0
app-name: Approov Shapes
app-version: 1.0
appid: ios.swift.shapes.demo.approov.io
app-sig: DRJNqt37tsdHwb9FKFT80dPqXlOHqZnZy68zAiVSsvM=
attest-reason: expiry
A stream of information for the the most recent filter matches is provided, with a separator providing the ID and the local capture time. Note also that the first property shown is matched-filters
which lists all of the filters that were matched by the device.
The device filtering information is collected and held by the Approov servers on a best effort basis. Only the information for the last 100 new devices is held and earlier entries are discarded. Note also that the information may be cleared from time to time so you should save it in a local file if you wish to keep it more permanently.
If you wish to obtain information for all filtered devices then the following short bash
script can be used. This appends to the approov-filtered.txt
file any filtered devices on a per-minute basis. This ensures that, unless there are more than 100 filtered devices per minute, all the information will be captured.
#!/bin/bash
while true; do
echo "getting filtered devices"
approov device -getInfo filtered -clear >> approov-filtered.txt
sleep 1m
done
The use of the -clear
option ensures that prior data is cleared on each call so there is no duplicated information.
Normally when a filter -matchValue
is specified it must be an exact verbatim match for the specified key’s value. However, it is also possible to do Regular expression matching if the -regex
option is used. For example:
$ approov filter -add android7 -key device -matchValue "Android 7" -regex
successfully set filter android7
This creates a filter that checks for the substring Android 7
in the device property. By default a specified regular expression performs a substring match. However, the full power of regular expressions may be used. There is a useful interactive tool here for constructing regular expressions.
Regular expression filtering can be particularly useful for checking if a device has particular properties in the flags-all
device information key. Simply specify the required flag name with -regex
and this will do a substring match to check if the flag is set, regardless of other flags (being careful to make sure the flag name is not itself a substring of some other flag name).
It is possible to create a filter that is only matched if a set of other filters all match. This allows the creation of more complex conditions that require matches across multiple different device information keys.
For instance, if you have previously setup a filter called android7
and another one called myip
, then you create a new filter that is the conjunction of these two as follows:
$ approov filter -add both-matched -conjunction android7+myip
successfully set filter both-matched
This new filter will only match is both the input filters are matched on the same device. You specify the set of input filters using a +
separated list provided to the command. Note that these inputs must be ordinary filters and not conjunction filters themselves.
When you first add a conjunction filter a check is made that the input filters are present. However, if these are subsequently removed then the conjunction filter will never match since all inputs must be present for a match to occur.
It is possible to set a user property on the Approov SDK. This user property is simply a string (of up to 128 characters) that is passed to the Approov cloud on all subsequent times that the device fetches an Approov token. It can be set as follows, presuming the value is available in property
:
Approov.setUserProperty(property);
Approov.setUserProperty(property);
This value will then appear with the key user-property
in the collected device information. Thus it is possible to create a device filter that matches certain values (or more complex patterns using the regex feature. Thus this can be used to highlight devices with particular properties, or even to ban them or mark them as being risky.
The string should be composed of printable ASCII characters only. Any other characters are mapped to ?
, including double quotes ("
) and forward slashes (/
).
User properties are only supported in version 2.5.0 and later SDKs.
All currently set filters can be shown as follows:
$ approov filter -list
2 filters:
myip ip: 1.2.3.4
special-app-sig app-sig: DRJNqt37tsdHwb9FKFT80dPqXlOHqZnZy68zAiVSsvM= newDIDOnly
The list of options associated with the filter are shown on the right hand side of the filter listing.
Any named filter can be removed as follows:
$ approov filter -remove special-app-sig
successfully removed filter special-app-sig
If you remove a filter that is an input to a conjunction filter, then it will never match until the input filter is replaced,
A -reject
option is provided so that any device which matches is automatically rejected, and will not receive a valid Approov token. For example:
$ approov filter -add myip -key ip -matchValue 1.2.3.4 -reject
WARNING: using the ban or reject option will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
successfully set filter myip
This rejects any device that attempts to fetch an Approov token from the IP address 1.2.3.4
.
You should be especially careful when using this option as it is available with dev
role priveleges. If a match is used that covers too many devices then this could cause large numbers of app users to be rejected. Always test the match without reject first and examine the metrics to determine the prevalence of the match.
A -ban
option is provided so that any device which matches is automatically banned. This sets the did-ban
flag on the device and it will not ever receive valid Approov tokens. For example:
$ approov filter -add myip -key ip -matchValue 1.2.3.4 -ban
WARNING: using the ban or reject option will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
successfully set filter myip
This bans any device ID that attempts to fetch an Approov token from the IP address 1.2.3.4
.
Note that any such ban will be permanently associated with the device ID, and the device ID will continue to be banned even if the filter is subsequently removed.
You should be especially careful when using this option as it is available with dev
role privileges. If a match is used that covers too many devices then this could cause large numbers of app users to be rejected. Always test the match without banning first and examine the metrics to determine the prevalence of the match.
In an emergency it is possible to clear all of the did-ban
states using the Clearing All Device State option. This requires an admin role. This clears all device state and thus undoes any prior device ID bans
A -risky
option is provided so that matching devices are automatically marked as risky, for example:
$ approov filter -add myip -key ip -matchValue 1.2.3.4 -risky
successfully set filter myip
This means any request from IP address 1.2.3.4
will have the risky-device
flag asserted.
The impact of this is two-fold:
risk
Annotation Policy is set then this means that the risky-device
flag will be set. This enables backend systems to observe this annotation and potentially perform different actions for the device.It is possible to use the special device ID value ++FilteredSecPolicy++w==
to provide a security policy that is applied to any device which matches specific filters. Firstly add this pseudo device ID as follows:
$ approov device -add ++FilteredSecPolicy++w== -policy alt-rules,default,default
successfully set device ++FilteredSecPolicy++w==
For the filtered policy to have an effect, you should not use the same security rules setting as the default for your account. The example alt-rules
shown above will not work (there are no such rules). Normally this feature is used in conjunction with Approov support staff for more detailed analysis of specific activity on your account and so you should really contact Approov support if you wish to use this feature.
You are not able to use the -pinMode
or -banMode
options on this special device ID. However, the -probeURL
value is applied.
In order to enable this behaviour for a particular filter you must use the -execFilteredSecPolicy
option, for example.
$ approov filter -add myip -key ip -matchValue 1.2.3.4 -execFilteredSecPolicy
successfully set filter myip
This causes any request from IP address 1.2.3.4
to execute the custom filtered security policy.
It is also possible to make a certain percentage of all devices execute the special filtered security policy, instead of the standard one for the account. You can simply set the percentage as follows:
$ approov filter -setSamplingPercentage 0.1
successfully set sampling percentage
This sets the sampling percentage to 0.1%. An admin
role is required to make this change. This means that 0.1% of all devices will execute the special filtered secuity policy rather than the standard account security policy. This determination is made on the basis of the device ID so that the same device will either be subject to the special policy or not. However, the selections change on each day so the sampling moves across the device population.
You can always see the current setting with:
$ approov filter -getSamplingPercentage
sampling percentage is 0.10%
Adding this special security policy causes a very small addition to the latency of Approov token fetches, independently of the number of devices that use it. Thus we suggest you only leave this special policy added for as long as you need to use it.
A record is kept of the device IDs of any apps that are fetching Approov tokens. This is held in time order, with an optional timestamp. It can be obtained with:
$ approov device -getFetches timed
qZka0yfv+ExvOq3PRh6pGw== 2020-07-23 12:10:45 BST
qZka0yfv+ExvOq3PRh6pGw== 2020-07-23 16:27:12 BST
w4mmGFZkpz5oPz6w6gl1Cw== 2020-07-23 16:27:20 BST
This is useful for analysis of the device IDs that are using the account, and perhaps frequency of use analysis to determine if particular device IDs are responsible for a high use of the account. These devices can then be added as custom devices for more detailed analysis if required. The parameter timed
indicates that timestamps should be included.
The token fetch device ID information is collected and held by the Approov servers on a best effort basis. Only the information for the last 1000 requests is held and earlier entries are discarded. Note also that the information may be cleared from time to time so you should save it in a local file if you wish to keep it more permanently.
A simple script such as the following can be used to collect this information into the local file approov-fetches.txt
:
#!/bin/bash
while true; do
echo "getting latest fetches"
approov device -getFetches untimed -clear >> approov-fetches.txt
sleep 1m
done
This simply captures the latest untimed
information every minute and uses the -clear
option to discard the obtained information so that it is not duplicated in the resultant file.
Information collected regarding fetches or the attributes of particular devices can be easily manipulated using standard commands. For instance, imagine we have captured approov-fetches.txt
showing two different devices using the account as follows:
qZka0yfv+ExvOq3PRh6pGw==
w4mmGFZkpz5oPz6w6gl1Cw==
w4mmGFZkpz5oPz6w6gl1Cw==
w4mmGFZkpz5oPz6w6gl1Cw==
w4mmGFZkpz5oPz6w6gl1Cw==
qZka0yfv+ExvOq3PRh6pGw==
qZka0yfv+ExvOq3PRh6pGw==
w4mmGFZkpz5oPz6w6gl1Cw==
w4mmGFZkpz5oPz6w6gl1Cw==
We can easily count the number of occurrences of each device ID as follows:
$ sort approov-fetches.txt | uniq -c
3 qZka0yfv+ExvOq3PRh6pGw==
6 w4mmGFZkpz5oPz6w6gl1Cw==
We can use this technique to anayze the frequency of use by certain device IDs or the frequency of occurrence of particular device attributes. This method can be used to identify commonly occurring device attributes, which can then be used in further searches in the original file to find the full context of each interesting match.
Note that this output can of course itself be piped to a file, such as output.txt
. You can then show the most commonly occurring repeats as follows:
$ sort -h -r output.txt | less
The -h
and -r
options cause the most frequently occurring lines to be shown first.
This section covers the optional token binding feature in Approov.
If you are able to use one of our Quickstart Frontend Integrations then these typically include support for token binding, by specifying a particular http
header that holds the source data for the hash. Similiarly, the Quickstart Backend Integrations are able to extract the same information from the header and verify that the correct token binding is included in the Approov token.
Token binding extends the security of the Approov token through a strength in depth approach. The mechanism ties an arbitrary string to an Approov token by including its hash in one of the claims. The facility is intended for long lived data, such as an OAuth
token or a user session identifier, that can be used to uniquely identify a user. The data is supplied to the Approov SDK as an ASCII encoded string. The SDK takes the string and calculates its SHA256 hash. This is then provided inside the Approov token in the pay
claim as a base64 encoded string.
If the binding data changes then this invalidates any currently cached Approov token and a new one is requested on the next token fetch. Thus it is important not to use rapidly changing data for binding. Ideally long lived user session tokens should be used that will likely not change during normal app usage.
Note that the hashing of the data sent in the pay
claim is always done in the SDK itself. Only the SHA256 hash of the data is sent to the Approov cloud service, so there is no need to be concerned about additional Personal Identifiable Information (PII) being sent to the Approov cloud service.
The data value may be set with the following method, where data
is a string representing the data to be bound:
Approov.setDataHashInToken(data);
Approov.setDataHashInToken(data);
This checks the SHA256 hash calculated for data
value against any current value that is held. If the value is different, then any cached Approov token is cleared. Any subsequent Approov token fetch call will obtain a new Approov token. The maximum rate at which new tokens can be fetched may be rate limited to prevent excessive usage if the binding data is modified and a token fetched in a tight loop.
The data binding is held in the pay
claim of the generated Approov token. The same value is retained in all subsequently generated Approov tokens until it is changed with another setDataHashInToken
call.
Note that once a pay
claim has been added it is not possible to remove its inclusion in the running app, only change its value.
The example token generation also supports the ability to generate a token with a pay
claim. This can be used to test backend integrations that must perform the same hashing calculation to compare the claim. For example we can use:
$ approov token -genExample your.api.com -setDataHashInToken custom-data
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJl…
You can decode the generated Approov token string as follows:
$ approov token -check eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJl…
valid: JWS {"did":"ExampleApproovTokenDID==","exp":1558640470,"ip":"1.2.3.4","pay":"tih+xRFV8PMsDhKthuFdvqWtQpKdT+K8X5W3258EJnU="}
This shows the pay
claim hash derived from the string custom-data
:
{
"did": "ExampleApproovTokenDID==",
"exp": 1558640470,
"ip": "1.2.3.4",
"pay": "tih+xRFV8PMsDhKthuFdvqWtQpKdT+K8X5W3258EJnU="
}
If the token binding features are used then the pay
claim must be checked on the backend to ensure the hash is consistent with the source data being presented. This data must be present in some other part of the request so that it can be extracted for comparison with the hash in the Approov token.
The recommended process for checking the pay
claim against the data provided in the request is as follows:
pay
claim. If claim is not present then the token may be from the Failover service.none
value which you both pass into the token fetch call in your app and in your server side payload check. This is because your backend code must cope with Failover tokens. Thus you must ensure normal tokens always have some content in the pay
claim or else the logic for handling failover tokens (which will only ever be issued in the unlikely case of an Approov primary outage) could provide a back door for avoiding a matching pay
claim.This section covers the integration between Approov and Apple’s DeviceCheck feature. If this integration is used then the Approov SDK forces the iOS device to generate a token that is passed to the Approov servers. This can then be checked with Apple and proves that the token was generated on a real iOS hardware device. This provides an additional level of protection against any spoofing attempts.
This feature is particularly useful if there is suspicion of malicious or fraudulent activity occurring via the continual re-installation of an app on the same physical device. The app itself may be unmodified and running in a clean environment so would receive a valid Approov token. The integration allows a specific physical iOS device to be banned once fraudulent activity has been detected on it so it is not able to receive valid Approov tokens again.
If this option is used then the iOS Approov SDK must make a call to generate a token to identify the device. This needs to be done on the first Approov token fetch after launching the app. Performing this operation requires additional network connection(s) and CPU processing time, over which the Approov SDK has no control. This can substantially delay the fetch operation on the iOS platform by adding up to 2500 ms of additional latency.
As discussed in Device ID Stability, the device ID reported on iOS automatically changes if an app is removed and then reinstalled. This is to preserve the privacy of the end user, as otherwise it would be possible to track an individual indefinitely via the device ID reported on their device. However, the inability to track specific devices creates some challenges with blocking specific malicious or fraudulent behaviour emanating from a particular device. If blocking is based on a device ID then the malicious actor can always circumvent any banning via reinstallation of an app. IP based blocking can also be evaded by the use of proxies.
Apple have recognized this as an issue and have introduced the DeviceCheck feature to provide some very limited tracking of specific physical devices that maintains user privacy. The facility allows up to two bits to be recorded for each physical device for each Apple developer account. These bits can be used to record information such as the detection of some fraudulent behaviour on the device.
Obtaining the bit status values and updates to them is performed by calling an Apple server API. In order to identify the device to this API it is necessary to obtain a Device Token. This is an ephemeral token (approximately 3KB in size) that identifies the device. Of course to prevent this itself being used to identify the device, and breaking the privacy policy, it is derived from a randomly generated nonce value so is different every time even on the same device. Since accessing the Apple API requires an authorization key this cannot be done in the app itself, since if this key was extracted it could be used to make arbitrary bit changes for other devices. Thus the API calls must be performed server side, necessitating that a protocol be established to communicate the DeviceCheck token from the mobile app to the server.
The Approov integration of DeviceCheck handles all of this integration complexity. If a DeviceCheck key is added to the Approov account, then the Approov SDK automatically collects and transmits the DeviceCheck tokens to the Approov servers. This token is then used to get and update the status of the device physical bits and prevent banned devices from being issued with valid Approov tokens. The simple check of the validity of the token also ensures that the traffic is emanating from a real iOS hardware device. Commands are provided to easily ban and and unban devices, and an optional automatic banning facility is available to automatically detect and ban devices associated with repeated app uninstalls and reinstalls.
The status of the DeviceCheck will be reflected in the device property flags:
devicecheck-unavailable
: Indicates the the DeviceCheck capability is not available on the device.devicecheck-completed
: DeviceCheck has been completed on a device.devicecheck-failed
: DeviceCheck has failed indicating that the provided device token was not valid.devicecheck-ban
: Indicates a permanent device ban associated with the physical device.In order to use the Approov DeviceCheck integration you need to add an Apple authentication key to your Approov account. This key only needs permissions to access and update the two bits provided for physical device. This is used by the Approov servers to dynamically query the Apple servers when new devices are added, and to update the settings of the bits if a particular device is being banned (or subsequently unbanned).
Log into your Apple Developer Account, click on Account
on the top menus, login and then navigate to the “Certificates, IDs & Profiles page”. Here you will see the list of keys and certificates that have been issued for your account, such as this below.
Select the “Keys” menu on the left hand side to navigate to the keys that have been issued for the account.
You need to click on the blue plus button to add a new key. Type in the name of that you want to call the key (you an choose anything that is meaningful to you) and click the option for DeviceCheck.
You then press the “Continue” button and a dialog will be shown asking you to confirm the key addition.
Click Register. You will then be provided with a dialog that gives you an option to download the private key that has been generated. Click this button. You will only get once chance to download this key so make sure the file is saved somewhere that is accessible for the next steps.
When the key is downloaded you will get a dialog such as the below with details of the key. The .p8
file itself should have been written to the Downloads
folder that your browser uses by default. Also make a note of the Team ID value shown on the top right of the screen next to your company name. You will also need that.
Now that you have a private key you need to make it accessible to Approov using the approov
command line tool.
$ approov devicecheck -addAuthKey AuthKey_XXXXXXXXXX.p8 -teamID YYYYYYYYY -bits bit0
WARNING: this will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
Where AuthKey_XXXXXXXXXX.p8
is the private key file you downloaded and YYYYYYYYY
is your team ID. In this case bit0
is selected so that the Approov service can read and update bit 0 of the two bits available per physical iOS device. You can change this to bit1
if you would rather use that bit instead. Note that if you do not specify -bits
then it is assumed that no bits are available and the device token is only used as an additional check that traffic is coming from a real iOS device.
Now that this information is added to your Approov account, the Approov cloud servers are able to contact the Apple servers to read and update the state. You can confirm that the information has been recorded with:
$ approov devicecheck -get
TeamID: YYYYYYYYY
KeyID: XXXXXXXXXX
Expiry: 12m
Bits: both
Apple Endpoint: Production
If there is a problem with the information provided, and it is not possible to authorize with the Apple servers then you will see WARNING: Apple Authorization Error
with the message from the Apple servers.
If you have selected usage of bits, then Approov sets those bits in order to indicate that a particular device has been banned, and should no longer receive valid Approov tokens. If you have never used the Apple DeviceCheck facility previously then the bits will not be set and so the default will be to allow the allow valid token fetching. If you have used the facility before and some bits may be set then this will cause banning of those devices. In this case you may use the -expiry
option when setting the authorization key to ignore settings made more than a certain number of months ago. If the bit setting has been more recent than one month then you will not be able to use this Approov facility on your apps until sufficient time has elapsed.
As discussed in Banning a Device, it is possible to ban a particular device ID from receiving valid Approov tokens. However, reinstallation of the app on the same device may generate a new device ID and evade the ban. If you are using DeviceCheck then a permanent ban for a particular physical device can be instigated.
Whenever you ban a device ID with DeviceCheck on the account active, the ban will become a permanent ban of the physical device (unless the bit availability has been set to none
).
If you have the all
Annotation Policy selected then tokens rejected due to a permanent DeviceCheck ban have devicecheck-ban
listed as a rejection reason.
Unbanning whatever device ID the physical device is using will also remove the permanent ban.
When a particular device is banned it is across all apps associated with the same Apple developer account, even if these apps are associated with different Approov accounts. This is because Apple stores the bit states on a per-developer account basis. If you are using multiple Approov accounts associated with a single Apple developer then you may select different bits on different accounts to avoid such interference.
The DeviceCheck authorization key can be removed from the account at any point, if an admin role is available, as follows:
$ approov devicecheck-remove
WARNING: this will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
device check information removed
This will disable all lookups of DeviceCheck. Any ban will remain on a particular device ID, but if the physical device is allocated a new device ID due to app reinstallation then the ban will not follow. If the device check key is subsequently set again then the banning of the same devices will be reinstated since the Apple bit state will not have been changed.
An automatic device banning feature is enabled if both
bits are made available when setting the DeviceCheck key, such as the following:
$ approov devicecheck -addAuthKey AuthKey_XXXXXXXXXX.p8 -teamID YYYYYYYYY -bits both
device check information added
In this case the two available bits are used as a counter of the number of times an app has been removed and reinstalled on a device. Each reinstallation will generate a new device ID. However the DeviceCheck bits implement a counter and when this reaches the maximum value of 3 the physical device will be automatically banned. This is because such continual re-installation behaviour is typically associated with some kind of malicious activity. Once banned the abuser will no longer be able to use the device to obtain valid Approov tokens. Non-malicious users would have to uninstall and reinstall the app at least three times before receiving a ban and this would represent very unusual behaviour.
Since the two bits are interpreted as a counter, we do not advise you to use this feature if the DeviceCheck bits have ever been previously used on the account for other purposes more recently than the value of -expiry
. Moreover, you should never use the automatic banning feature if sharing a single Apple developer account across multiple Approov accounts or if you have multiple different iOS apps associated with even a single Approov account.
Note that once a device has been banned, the only way to unban it is via the manual Removing Device Ban method.
This section covers the integration between Approov and Google’s SafetyNet attestation feature. It allows enhanced assessment of the integrity of the device, and provides additional signals that Approov uses to check for rooting or modified devices.
Use of SafetyNet requires Google Play Services to be active on the device. Not all Android devices have Play Services installed, or able to support it. Thus you should be careful about assuming that SafetyNet is universally available for your Android user base. Note also that in order to use SafetyNet your app must specify the appropriate packages in the gradle build.
In order to use SafetyNet then you must use an SDK that is version 2.4.0 or later. If this option is used then the Android Approov SDK must make a call to perform an attestation on the device. This needs to be done on the first Approov token fetch after first installing the app. Performing this operation requires additional network connection(s) and CPU processing time, over which the Approov SDK has no control. This can substantially delay the fetch operation on the Android platform by adding up to 3500 ms of additional latency.
If SafetyNet is enabled then the Approov SDK initiates an attestation the first time that app is launched after installation. The amount of time that this takes is highly dependent upon the device and can take several seconds. In order to perform this attestation, a SafetyNet API key must be added to your account. The attestation involves analysis of the device filesystem integrity and a communication with Google servers. Upon success an SafetyNet token is provided, which is transmitted to the Approov servers and forms part of the overall enhanced attestation process that it performs.
In order to prevent any possibility of a replay of known good SafetyNet tokens, it contains a nonce value that is generated by the Approov servers. This is checked as part of the overall analysis of the SafetyNet token to ensure that it is a live token generated as part of the current attestation transactions.
The full validity of the SafetyNet token is checked, including analysis of its certificate chain to ensure that the token was definitely issued by Google servers. This does not require any communication between Approov and Google servers.
If there are any issues with the SafetyNet token obtained, then these are reflected in the device property flags that are collected as follows:
safetynet-completed
: Indicates that a SafetyNet token has been received by Approov.safetynet-fail-token
: Indicates that the SafetyNet token is invalid in some way, such as not being signed correctly or the nonce value is not as expected. This almost certainly indicates some kind of hacking attempt.safetynet-fail-integrity
: Indicates that the SafetyNet basicIntegrity
flag was false. This indicates that the device is likely to have been tampered with in some way, and is likely to be rooted or an emulator.safetynet-fail-profile
: Indicates that the SafetyNet ctsProfileMatch
flag was false. This means that the device is not running a version of Android that has passed Google’s compatibility testing.safetynet-fail-cert
: Indicates that the apkCertificateDigestSha256
field in the SafetyNet token does not match an expected app signing certificate. This test is done in addition to the normal Approov operation which also checks the authenticity of the app. This flag can only be asserted if Android App Signing Certificates have been setup for the account.safetynet-unavailable
: Indicates that SafetyNet was not available. This may be because it is not supported on the device, the Google quota has been exceeded or because the app has not been built to include the necessary SafetyNet Google library (see App Build Requirements).These flags are used in various Rejection Policies to determine if a valid Approov token should be issued. Note that most rejection policies do not make the receipt of a valid SafetyNet token mandatory. However, the policy strict
does do so but you should be aware that any devices unable to perform a SafetyNet check will be unable to get a valid Approov token.
Since obtaining a SafetyNet token is an expensive operation, the results are recorded by Approov and are persisted against the device ID. This means that the app should only suffer the extended latency to obtain a SafetyNet token on the initial launch after installation. You might consider prefetching an Approov token in the background to make this delay less obvious.
It might not be possible obtain a SafetyNet token, likely due to one of the following reasons:
In this case subsequent Approov token fetches do not retry, However, each time the app on the device is restarted a fresh attempt will be made until a SafetyNet attestation has been successfully performed.
Note that the SafetyNet API is transmitted dynamically to your running apps. This allows you to change the API key without having to redeploy your apps. Futrhermore, since the API key is not held statically in your app’s APK image it cannot be reverse engineered and attempts made to abuse it to exceed your usage quota.
You need to Obtain an API key from Google in order to use the SafetyNet API. This key must then be made available in your Approov account.
There are a number of steps to this process. Firstly, if you have not used Google APIs in your developer account previously then you need to follow the instructions in Getting Started. You will need to enable API access and link it to your developer account in the following screen:
Go to the API Library page in the Google console for your developer account:
Search for and then select the Android Device Verification API
. The dashboard screen then appears:
Click the button to enable it. You should then see a screen like this:
This shows a graph of the usage of the API which you may consult later once SafetyNet is in use. Next you will need to obtain a credential (i.e. SafetyNet API Key) to use it. If the Create Credentials
button appears, click on it to generate an API key. Otherwise, click the All API Credentials
drop-down list, then select the API key that’s associated with your project that has enabled the Android Device Verification API as follows:
Whichever route you take to get the API key, you should ultimately end up with a dialog such as this that provides the API key:
Copy and paste this API key as you will need to add it to your Approov account. Remember you ever revoke this API then you will need to update your Approov account with the new one, and Approov SafetyNet attestations cannot be performed in the meantime.
Set the SafetyNet API key as follows:
$ approov safetynet -set XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
WARNING: this will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
safetynet API key set
Once this is set Android devices will immediately try and perform SafetyNet attestations when they connect to the Approov service and they have not done so previously. Note that these means that any existing users of your app will perform a SafetyNet attestation the next time they connect, so consider this when thinking about your initial load against your SafetyNet API quota.
You can always to determine the SafetyNet key that has been set with:
$ approov safetynet -get
APIKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Once you have set your SafetyNet API key, we suggest you launch your app using Approov and then check that [device’s state] (#getting-state-of-a-device) to ensure it has a safetynet-completed
property. You can also see SafetyNet related flags in the Metrics Graphs.
The SafetyNet API key can be removed as follows:
$ approov safetynet -remove
WARNING: this will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
safetynet information removed
This will immediately stop any usage of the SafetyNet API by your apps. The persisted SafetyNet state for any devices that have conducted a SafetyNet attestation will be retained. If you wish to completely remove any persisted state which may be causing unwanted Approov rejections, for instance, then consider Clearing All Device State.
The SDK .aar
cannot provide dependencies on the external libraries that it needs to use. Thus if you wish to make use of SafetyNet you must add the appropriate libraries in the dependencies
part of your app’s build.gradle
file as follows:
implementation 'com.google.android.gms:play-services-base:17.3.0'
implementation 'com.google.android.gms:play-services-safetynet:17.0.0'
If the Approov SDK inside your app is unable to resolve dependencies to the required Google Play services, then the message Google GMS services not available
or Google SafetyNet not available
will be logged when the Approov SDK is initialized. We suggest you check your integration by launching the app with a SafetyNet API key set and then Getting State of a Device using the device’s ID. If SafetyNet has been successfully carried out then you will see safetynet-completed
as one of the properties of the device.
If you are currently using Appcompat in your app then you may be forced to update older compat-v7
versions to allow use of the latest Google Play Services, see this.
The SafetyNet token may include a apkCertificateDigestSha256
claim that provides a SHA256 hash of the certificate used to sign the application. This claim might not be present if the device does not meet even basic integrity requirements. This is in contrast to the Approov app signature analysis which operates even if a device is rooted.
If this claim is present and the signing certificates have been added to the Approov account using approov appsigncert
then a comparison is made as an additional check to ensure that the SafetyNet token is consistent with the identity of the calling app. If not then the flag safetynet-fail-cert
is set.
Performing a SafetyNet attestation causes the device to call a Google API. By default, accounts have a fixed quota of 10,000 requests per day across all apps using the same SafetyNet API key.
Approov will only perform a SafetyNet attestation the first time an app is launched after installation (or after setting the SafetyNet API key, if there is already an installed base of app users). The SafetyNet status is persisted by Approov servers to avoid having to use SafetyNet more frequently. Thus if your typical increase in new users is less than 10,000 per day you can use the standard quota.
If you expect higher traffic than this then you must request an increased quota from Google. When you reach the following questions you can fill them in as follows:
The Approov integration already takes the appropriate measures to minimize the usage of the SafetyNet service.
If you exceed your quota then some devices will not be able to perform a SafetyNet attestation. This is not a problem necessarily (unless you are using the strict
rejection policy) as the SafetyNet attestation will be performed the next time the app is restarted and connects to the Approov service. This should help smooth the consumption of your quota. It is only problematic if you are continually exceeding your quota on a day-to-day basis.
Note that there is also a limit of 5 requests per minute from an individual device. However, the design of Approov is such that this limit should never be exceeded.
If you have a single API which accepts requests from both the web and mobile channels, then you may appreciate the ability to have shared code for granting access to protected data. The Approov flow has been extended with a facility for web-protection-requests to enable this. The server-side flow of several, best in class, browser solutions have been integrated into Approov to communicate their results back to browsers wrapped in an untamperable Approov token. At a minimum, this enables a single simple token check in the API backend to authorize access. However, if required, Approov can be configured to embed the full results of the integrated protection to allow for deeper interrogation.
There is support for the following web protection services: FingerprintJS, Google reCAPTCHA, and hCaptcha.
To use an integration, you must signup for the associated service but then follow modified instructions for using those services described in the associated section under Implementing the Integrated Services Flow.
Just because you can doesn’t mean you should. The primary Approov service can provide a very strong indication that a request originates from an untampered instance of your app running on a user device. The integrated web services are not attempting to solve this issue directly and therefore cannot provide the same level of confidence, even if combining multiple integrations. (Even without a specific attack, the built in debug facilities of browsers make it too easy for users to copy tokens, or other data, that a service provider would prefer to be kept private.) As such, we recommend that you restrict critical API endpoints to only work from the Mobile App. The solution presented here, supports this: after successfully verifying a token signature, you can always look at the token claims to determine if it was issued for the web or mobile channel and use that to inform your access control logic.
In some cases, it may be necessary to have a more involved access check. For example, the mobile app token includes a did (DeviceID) claim that uniquely identifies the particular install of an app. If required, the protected API can associate this ID with a user’s account. Such an association can then be checked on each API request to identify those requests that don’t match and then highlight them as having a heightened risk of fraud. The mismatch can be used to trigger additional security steps that determine if the difference is caused by an attempt to misuse the credentials from another valid app instance, or because the legitimate account owner is logging in from a different mobile device.
In the web-flow we can achieve the same kind of check by embedding the results of a FingerprintJS lookup in the Approov token. This exposes the FingerprintJS visitorID to the API backend, enabling the same kind of association as the DeviceID and a corresponding security flow when a new visitorID is identified for a user. A similar binding is also possible using the user identifiers available to enterprise level hCaptcha customers. In a similar vein, the Google reCAPTCHA or hCaptcha services can be used to provide confidence that requests have not been scripted.
The combined Mobile and Web flows are shown in the following diagram:
The left side of the diagram illustrates the primary Approov mobile app protection flow:
The right side of the diagram shows the parallel web protection flow, with one or more integrations in place and using a common backend API:
The following steps are required to setup a web protection service.
The first two steps, dealing with server side changes, are covered in the next section. Steps 3 through 7 are specific to the integrated service and the sections, FingerprintJS, Google reCAPTCHA, and hCaptcha take you through those steps for each of the associated services. The section on Web Protection Metrics Presentation covers step 8 and completes the basic flow. A final section on web protection provides a reference for the service endpoints and also covers more advanced topics: using multiple integrated services, obtaining tokens for multiple API calls in one request, adding token binding to your web app, and troubleshooting.
The first step is to audit the target API(s) to decide which endpoints or queries will service both web and mobile requests and those that will be unique to one or the other. As mentioned previously, web protections do not provide the same level of confidence as the Approov mobile API protection service, that a request has not been constructed by a threat actor. The table below shows the claims that can be used to distinguish between a token issued to a mobile app and one issued to a web app:
Claim | Description |
---|---|
did |
The did claim is present in all tokens obtained from the mobile app attestation channel. It identifies the unique ID for attesting device. Testing for it's presence is the easiest way to identify tokens originating from this channel whether they come from the Primary service or from the Failover. |
embed |
The embed claim is present in all tokens obtained from the web protection channel. It holds a JSON object whose keys indicate the specific web protection service, or services, that were used to obtain the token and whose values are brief summaries, or the full results, obtained from the wrapped services. If it is important to query the results obtained from the 3rd party service in your backend, then you can also use the presence of the claim itself to determine which channel created the token. Note that, because the full web protection results may contain entries that must be kept private in order to maintain solution security, you must use encrypted tokens (JWEs) to include it in the generated Approov token. |
Note that, in either case you should first ensure that the token you have received is valid, signed in the expected way with the correct secret. See the section on Backend integrations.
If you have an existing mobile only API that you are considering opening up to the web channel, then it is imperitave that you first perform some kind of end-point audit to identify those API entries that could be enabled for web-access, those that shouldn’t, and those that mustn’t. As mentioned previously, Approov mobile API protection provides a strong degree of confidence that requests originate from legitimate installs of your apps. This confidence cannot be reproduced for the web channel.
Use the Approov CLI to enable the web-flow for your APIs. The section on adding APIs with the Approov CLI covers all the options available when adding an API. Web protection is included in that documentation, however, in short, web protection requests are enabled for an API when it is added with the -enableWeb
flag. If you wish to enable web protection for an existing API definition you just add it again using the same parameters as before but also including the -enableWeb
flag. To disable web protection, you add the API again but without the flag. Each addition of the same API, completely overwrites the previous settings for that API.
$ approov api -add mydomain.com -enableWeb
WARNING: adding the API will have an immediate impact on your apps in production.
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain mydomain.com with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
To receive a token at all, all web protection requests must include at least one web-enabled target API. So, if you are adding your first integrated service it may be useful to first add a web-enabled development API domain that you can include in your test requests.
Note that, if you intend to embed the full results from the integrated protection server-side lookup, then you must also use encrypted tokens for the API to prevent leaking potentially sensitive data from the integrated protection. Only APIs configured to use encrypted tokens will get the full results embedded in the token. An API will use an encrypted token if it is configured to use a JWK with an encryption algorithm, or, if you use the -jwe
flag, then the default encryption secret for your account will be used to encrypt tokens with the A256GCMKW algorithm:
$ approov api -add mydomain.com -enableWeb -jwe
WARNING: adding the API will have an immediate impact on your apps in production.
ATTENTION: If you wish to continue then please type YES and return: YES
added API domain mydomain.com with type:account, alg:HS256, pin:JG29gD8vWiEanTCVPjYLQ5yiYMKQqR05OH38yFf0kBU=
enabled continuous monitoring for https://mydomain.com:443 (remove using `approov monitoring -removeAPI`)
You must change your back-end server to accept the new token type before changing the token signing algorithm for your active APIs, including the addition of the -jwe flag. Ideally, the server should be altered to accept both the new and old token type while the new token format propagates through the flow.
Several sections provide details on handling APIs, secrets, and encryption: Adding API Domains, Managing Keysets, JWE Token Encryption, and Account Secret Key Export.
FingerprintJS provides a powerful mechanism for fingerprinting the browser environment of a user and deriving a visitor ID based on both the raw fingerprint and the history of prior visits for that user. This visitor ID can then be compared against a user identity in the backend system to determine if they match. If they do, then there is a high probability that it is indeed the correct user and operations can proceed. If not, then this may indicate an attempt at account takeover or simply that the user has moved to a different browser environment. In either case, additional verification steps should be introduced into the flow to protect the user’s account.
Before proceeding with an integration you must signup for an account: FingerprintJS Signup. Each FingerprintJS account can have one or more subscriptions and each subscription needs a browser token and an API token to work. You should follow the most appropriate JavaScript agent documentation to the point where you obtain an fp.get()
result.
Importantly, for best results from the FingerprintJS service, you should follow the instructions for adding a subdomain to handle FingerprintJS queries: Subdomain setup.
A FingerprintJS subscription can be added by specifying the subscription region and by providing a valid Browser and API token. You can add multiple subscriptions: the browser token is a public key to identify the target subscription for a web protection request. The following command would add a subscription in the Rest-of-the-World(RoW) region leaving all other settings at their default values:
$ approov web -fingerprintjs -add <FingerprintJS-BrowserToken> -apiToken <FingerprintJS-APIToken> -region RoW
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
fingerprintjs subscription <FingerprintJS-BorwserToken> added
Listing all the configured web integration properties shows that the new subscription has been added:
$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
FingerprintJS:
Optional: true
Subscription Key: <FingerprintJS-BrowserToken>
Region: RoW
Max Elapsed Time: 2.00s
Max Bot Probability: 1.00
Embed Result: false
The following table lists properties in order and provides their description:
Property Name | Description |
Site Key | This is the Approov site key you need to use to identify your Approov account in web protection API requests |
Token Lifetime | This is the lifetime given to Approov tokens obtained from web protection. It may be changed using approov web -setTokenLifetime <seconds> . |
FingerprintJS | The heading that separates FingerprintJS specific properties from general properties and those of other integrations. |
Optional | Indicates whether a FingerprintJS result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection using another configured integration even if a FingerprintJS result is not provided. This property may be unset using approov web -fingerprintjs -setRequired and reset using approov web -fingerprintjs -setOptional . |
Subscription Key | Displays the <FingerprintJS-BrowserToken> used to configure the subscription and serves as the heading for properties related to just this subscription. Note that the <FingerprintJS-APIToken> is not listed; the API token is the private key for the subscription and it is never displayed or logged by Approov. If you use the FingerprintJS dashboard to change the API Token for a subscription, then you can always update the Approov subscription by adding it again with the new API token. |
Region | The configured region for the subscription, either RoW or EU . This must match the region specified for the subscription in the FingerprintJS dashboard. |
Max Elapsed Time | Specifies the maximum permitted time between a FingerprintJS get call and the subsequent Approov web protection request. The 2 second default matches the delay enforced by the Google reCAPTCHA and hCaptcha flows. To change this value, use the -maxElapsedTime <duration> parameter when you add the subscription. |
Max Bot Probability | One of the values returned from a FingerprintJS server-side lookup is a BotProbability score. Approov will return invalid tokens if the FingerprintJS lookup returns a score higher than the configured max. The default, 1.0, will accept all scores. To change this value, use the -maxBotProbability <score> parameter when you add the subscription. |
Embed Result | If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full FingerprintJS result object will be included in generated Approov tokens. It defaults to false , use -embedResult when you add a subscription to turn it on. |
Rate Limit | This is not in the above list because no rate limit was specified, but it may be configured when you add a subscription to specify the maximum number of requests per minute: -rateLimit <limit> . Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed. |
To change a subscription you simply add it again with all the properties required for the changed subscription. Each addition of the same browser token completely overwrites the previously stored entry.
Once the subscription is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:
async function fetchApproovToken(visitorId, requestId) {
const params = new URLSearchParams()
params.append('approov-site-key', '<Approov-Site-Key>')
params.append('api', '<Target-Approov-Protected-API-Domain>')
params.append('fingerprintjs-token', '<FingerprintJS-Browser-Token>')
params.append('fingerprintjs-visitor-id', visitorId)
params.append('fingerprintjs-request-id', requestId)
const response = await fetch('https://web-1.approovr.io/attest', {
method: 'POST',
body: params
})
if (!response.ok) {
throw new Error(response) // reject with a throw on failure
}
return response.text() // return the token on success
}
With a small modification the example code provided by FingerprintJS can be adapted to call this function:
Excerpt from Original FingerprintJS Integration
// Initialize an agent at application startup.
const fpPromise = FingerprintJS.load({ token: 'your-browser-token' })
// Get the visitor identifier when you need it.
fpPromise
.then(fp => fp.get())
.then(result => console.log(result.visitorId))
FingerprintJS/Approov Integration
// Initialize an agent at application startup.
const fpPromise = FingerprintJS.load({ token: 'your-browser-token' })
// Get the visitor identifier when you need it.
fpPromise
.then(fp => fp.get())
.then(result => fetchApproovToken(result.visitorId, result.requestId))
.then(token => {
// perform the API call adding the Approov token
})
.catch(err => console.log('Failed FingerprintJS/Approov promise chain: ' + err))
Note that the Approov token should never be logged in production code.
embed
token claim for FingerprintJSYou may wish to interrogate the FingerprintJS results in your backend. FingerprintJS properties are added to the embed
claim of the Approov token which is a JSON object with a property called fpjs:<BrowserToken>
, where the BrowserToken is the FingerprintJS Browser Token registered to your account and used in the Approov token request. By default only the visitorId and requestId are included in the token (using the same JSON structure as the full FingerprintJS result). The following example shows the full decoded body of an Approov token with the default properties embedded from a FingerprintJS request lookup:
{
"exp": 1620146455,
"ip": "1.2.3.4",
"arc": "LL3BRGIELU",
"embed": {
"fpjs:wdATWfQIsdYwATBYYIch": {
"visitorId": "eZsCHxhztqEOX0ZmOlwi",
"visits": [
{
"requestId": "lsodLAU4ubccLnMzrI3N"
}
]
}
}
}
If the -embedResult
flag is used when you register the FingerprintJS subscription and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. A slightly abbreviated example is shown below:
{
"exp": 1620128085,
"ip": "1.2.3.4",
"arc": "EAM2W37PSU",
"embed": {
"fpjs:wdATWfQIsdYwATBYYIch": {
"visitorId": "eZsCHxhztqEOX0ZmOlwi",
"visits": [
{
"requestId": "Otc6rGJcax7PrUuby7GA",
"browserDetails": {
"browserName": "Chrome",
"browserMajorVersion": "90",
"browserFullVersion": "90.0.4430",
"os": "Linux",
"device": "Other",
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36",
"botProbability": 0
},
"incognito": false,
"ip": "1.2.3.4",
"ipLocation": { "<IP-keys...>": "<IP-values...>" },
"time": "2021-05-04T11:32:30Z",
"timestamp": 1620127950391,
"url": "<URL-of-the-calling-webpage>",
"tag": {}
}
]
}
}
}
You should now be setup to generate and receive web protection Approov tokens using FingerprintJS. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the reference section after the integration summaries.
Google reCAPTCHA is a popular service to determine if a browser is being operated by a human. A browser request retrieves a token from the reCAPTCHA service which is then passed to the protected API as part of a request to be processed. A second request from the API backend obtains the full set of results associated with the token and uses this to determine whether to accept or reject the original request.
Before proceeding with an integration you must signup for a key-pair; there’s a link from the developer guide introduction: Google reCAPTCHA. The Approov integration supports v2(tickbox or badge) and v3. Each key-pair has a site-key and a secret which you will use to register the site with Approov. Continue to follow the developer guide for your selected reCAPTCHA flow; the instructions below will show how to modify the flow to add an Approov web protection request.
A site can be added to Approov by providing the reCAPTCHA site-key and the associated secret. You can add multiple sites: the site-key is the unique key that identifies the target site for a web protection request. The following command adds a site to your Approov account leaving all other settings at their default values:
$ approov web -recaptcha -add <reCAPTCHA-Site-Key> -secret <reCAPTCHA-Secret>
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
recaptcha site <reCAPTCHA-Site-Key> added
Listing all the configured web protection properties shows that the new site has been added:
$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
reCAPTCHA:
Optional: true
Site Key: <reCAPTCHA-Site-Key>
Min Score: 0.00
Include IP: false
Embed Result: true
The following table lists properties in order and provides a description:
Property Name | Description |
Site Key | This is the Approov site key you need to use to identify your Approov account in web protection API requests |
Token Lifetime | This is the lifetime given to Approov tokens obtained from a web protection request. It may be changed using approov web -setTokenLifetime <seconds> . |
reCAPTCHA | The heading that separates reCAPTCHA specific properties from general properties and those of other integrations. |
Optional | Indicates whether an reCAPTCHA result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection request using another configured integration even if a reCAPTCHA result is not provided. This property may be unset using approov web -recaptcha -setRequired and reset using approov web -recaptcha -setOptional . |
Site Key | Displays the <reCAPTCHA-Site-Key> used to configure the site and serves as the heading for properties related to just this site. Note that the <reCAPTCHA-Secret> is not listed; the secret is the private key for the site and it is never displayed or logged by Approov. |
Domains | This lists the domains registered against the site; the entry is omitted if no domains were specified. Multiple domains can be registered by using the -domain flag multiple times. The backend reCAPTCHA lookup performed by Approov returns the domain that performed the reCAPTCHA. If one or more domains are defined, then the returned domain must be found in this list. If no domains are registered, then a domain check is not performed. This facility can be used to selectively enable some of the domains that you have registered with the reCAPTCHA site in Google. |
Actions | This is not in the above list because no actions were specified as the site was added but the entry lists the actions registered against the site using the -action <value> argument. Multiple actions may be registered by using the -action flag multiple times. The backend reCAPTCHA lookup performed by Approov may return the action that performed the reCAPTCHA(v3 only); if the list of actions registered with Approov is non-empty, then the returned action string must be in the list for Approov to return a valid token. If no actions are registered, or if no action is provided by the backend reCAPTCHA lookup, then the check is not performed. |
Min Score | The Score returned from a reCAPTCHA server-side lookup represents the probability that the website user is a human. Approov will return invalid tokens if the reCAPTCHA lookup returns a score lower than the configured min. The default, 0, will accept all scores. To change this value, use the -minScore <score> argument when you add a site. |
Embed Result | If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full reCAPTCHA result object will be included in generated Approov tokens. It defaults to false , use -embedResult when you add a site to turn it on. |
Rate Limit | This is not in the above list because no rate limit was specified, but it may be configured when you add a site to specify the maximum number of requests per minute: -rateLimit <limit> . Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed. |
To change a site’s properties you simply add it again with all the properties required for the change. Each addition of the same site-key completely overwrites the previously stored entry.
Once the site is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:
async function fetchApproovToken(recapToken) {
const params = new URLSearchParams()
params.append('approov-site-key', '<Approov-Site-Key>')
params.append('api', '<Target-Approov-Protected-API-Domain>')
params.append('recaptcha-site-key', '<reCAPTCHA-Site-Key>')
params.append('recaptcha-token', recapToken)
const response = await fetch('https://web-1.approovr.io/attest', {
method: 'POST',
body: params
})
if (!response.ok) {
throw new Error(response) // reject with a throw on failure
}
return response.text() // return the token on success
}
The Approov token should never be logged in production code.
The precise code required to obtain the reCAPTCHA token and then call the above function will depend on the reCAPTCHA version and mechanism that you have selected. In each case, after obtaining the reCAPTCHA token, you next pass it to fetchApproovToken
before passing the resulting Approov token to your backend. The discussion below focuses on changes to the Google docs for reCAPTCHA v2 Checkbox style, however, the transformations are applicable to any of the reCAPTCHA flows.
The simplest example provided for reCAPTCHA v2 Checkbox style, Automatically render reCAPTCHA example, is to introduce a div
with the g-recaptcha
class inside the form in which the reCAPTHCA challenge will be issued. In this approach, the HTML in the docs doesn’t retrieve the reCAPTCHA token that results from the challenge, instead it is embedded in the form data and submitted from there. To use Approov in the same style, you will need to ensure that the Approov token is also added to a hidden form input field before the form is submitted. One approach for this, dervied from Google example, is provided below:
<html>
<head>
<title>reCAPTCHA demo: Simple page</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<script>
async function fetchApproovToken(recapToken) {
// as above...
}
function dataCallback(recapToken) {
fetchApproovToken(recapToken)
.then(approovToken => {
const tokenEl = document.getElementById('approovToken')
tokenEl.value = approovToken
enableSubmitCheck()
})
}
function enableSubmitCheck() {
// a function triggered whenever we need to check if the submit button
// should be enabled. Note that this is important, so that we can check
// for the presence of the Approov token before the form is submitted.
let shouldDisable = false
// Other checks here ...
const tokenEl = document.getElementById('approovToken')
if (tokenEl.value == "") {
shouldDisable = true
}
const submitEl = document.getElementById('submit')
if (shouldDisable) {
submitEl.setAttribute('disabled', true)
} else {
submitEl.removeAttribute('disabled')
}
}
</script>
<form action="?" method="POST">
<div class="g-recaptcha" data-sitekey="<reCAPTCHA-Site-Key>" data-callback="dataCallback"></div>
<br/>
<input id="approovToken" type="hidden" value="" name="approovToken"></input>
<input id="submit" type="submit" value="Submit" disabled>
</form>
</body>
</html>
The downside of this approach is that a user action is required (clicking the submit button) between the event that retrieves the approovToken and the form submission. This means that the default Approov token lifetime is likely to be too short. Extending the token lifetime is not recommended unless network latency is causing token exipiry before it can be processed by your backend API. The preferred solution is to override the form’s default submit behavior so that the Approov token fetch can be inserted in that flow. This second approach requires that your javascript code construct and issue the request to your API, however, this is also best practice because you can then adjust the request to precisely match the one used by your mbile app (which is highly likely to include some properties in request headers, like the Approov token).
A modified version of the above, may look something like the example below:
<html>
<head>
<title>reCAPTCHA demo: Simple page</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<script>
async function fetchApproovToken(recapToken) {
// as above...
}
function submitForm() {
const recapToken = grecaptcha.getResponse()
if (recapToken != "") {
fetchApproovToken(recapToken)
.then(approovToken => {
// construct fetch request for API call ...
})
.catch(err => {
// handle token fetch error
})
}
}
</script>
<form action="javascript:submitForm()">
<div class="g-recaptcha" data-sitekey="<reCAPTCHA-Site-Key>"></div>
<br/>
<input id="submit" type="submit" value="Submit">
</form>
</body>
</html>
Other examples provided in the reCAPTCHA documentation can also be adapted in a similar vein to the one described.
embed
token claim for reCAPTCHAYou may wish to interrogate the reCAPTCHA results in your backend. Google reCAPTCHA properties are added to the embed
claim of the Approov token which is a JSON object with a property called recap:<SiteKey>
, where the SiteKey is the reCAPTCHA site-key registered to your Approov account and used in the Approov token request. By default only the challenge timestamp and the domain properties are included in the token (using the same JSON structure as the full reCAPTCHA result), thus confirming that a reCAPTCHA lookup occurred. The following example shows the full decoded body of an Approov token with the default properties embedded from a reCAPTCHA request lookup:
{
"exp": 1620398492,
"ip": "1.2.3.4",
"arc": "EDZ2BABJIO",
"embed": {
"recap:6Ldz3WgaAAAAAA7wWZEgs1J_hZabCrBjgZ5Wz3YM": {
"challenge_ts": "2021-05-07T14:39:15",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
If the -embedResult
flag is used when you register the Google reCAPTCHA site and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. Note that the results from the Google reCAPTCHA lookup vary depending on the version. A slightly abbreviated example is shown below:
{
"exp": 1620653328,
"ip": "1.2.3.4",
"arc": "IACJXOWMRO",
"embed": {
"recap:6Ldz3WgaAAAAAA7wWZEgs1J_hZabCrBjgZ5Wz3YM": {
"success": true,
"challenge_ts": "2021-05-10T13:26:30Z",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
You should now be all setup to generate and receive web protection Approov tokens using Google reCAPTCHA. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the reference section after the integration summaries.
hCaptcha is a popular service to determine if a browser is being operated by a human. A browser request retrieves a token from the hCaptcha service which is then passed to the protected API as part of a request to be processed. A second request from the API backend obtains the full set of results associated with the token and uses them to determine whether to accept or reject the original request.
Before proceeding with an integration you must signup for an account: hCaptcha signup. Each hCaptcha account has a secret and it can have one or more sites with each site having a unique site-key. You should follow the Developer guide to either insert the hCaptcha widget, or use the invisible captcha approach. In either case, the instructions below will show how to add Approov web protection to the flow.
An hCaptcha site can be added by providing the hCpatcha site-key with the associated secret for your account. You can add multiple sites: the site-key is the unique key that identifies the target site for a web protection request. The following command adds a site to your Approov account leaving all other settings at their default values:
$ approov web -hcaptcha -add <hCaptcha-Site-Key> -secret <hCaptcha-Secret> -domain <web-site-domain>
WARNING: changing the web configuration will have an immediate impact in production
ATTENTION: If you wish to continue then please type YES and return: YES
hcaptcha site <hCaptcha-Site-Key> added
Listing all the configured web protection properties shows that the new site has been added:
$ approov web -list
Site Key: <Account-Site-Key>
Token Lifetime: 120 seconds
hCaptcha:
Optional: true
Site Key: <hCaptcha-Site-Key>
Domains:
<web-site-domain>
Min Score: 0.00
Include IP: false
Embed Result: false
The following table lists properties in order and provides their description:
Property Name | Description |
Site Key | This is the Approov site key you need to use to identify your Approov account in web protection API requests |
Token Lifetime | This is the lifetime given to Approov tokens obtained from a web protection request. It may be changed using approov web -setTokenLifetime <seconds> . |
hCaptcha | The heading that separates hCaptcha specific properties from general properties and those of other integrations. |
Optional | Indicates whether an hCaptcha result must be provided for every web protection request or not. This defaults to true which allows a valid Approov token to be provided for a web protection request using another configured integration even if an hCaptcha result is not provided. This property may be unset using approov web -hcaptcha -setRequired and reset using approov web -hcaptcha -setOptional . |
Site Key | Displays the <hCaptcha-Site-Key> used to configure the site and serves as the heading for properties related to just this site. Note that the <hCaptcha-Secret> is not listed; the secret is the private key for the site and it is never displayed or logged by Approov. If you use the hCaptcha dashboard to change the site-key or secret, then you can always update the Approov site by adding it again with the new site-key. |
Domains | This lists the domains registered against the site; the entry is omitted if no domains were specified. Multiple domains can be registered by using the -domain flag multiple times. The backend hCaptcha lookup performed by Approov returns the domain that solved the hCaptcha challenge. If one or more domains are defined, then the returned domain must be found in this list. If no domains are registered, then a domain check is not performed. This facility can be used to selectively enable some of the domains that you have registered with the hCaptcha site definition. (We note that, when hCaptcha returns the domain, it strips the subdomain labels from the root. Please take care to ensure that the domains you register with Approov match the domains returned by hCaptcha results.) |
Min Score | The Score returned from an hCaptcha server-side lookup represents the probability that the user that solved an hCaptcha challenge is a human. Approov will return invalid tokens if the hCaptcha lookup returns a score lower than the configured minimum. The default, 0, will accept all scores. To change this value, use the -minScore <score> parameter when you add a site. |
Embed Result | If this is true *and you have chosen to use encrypted tokens*, JWEs, for the target API then the full hCaptcha result object will be included in generated Approov tokens. It defaults to false , use -embedResult when you add a site to turn it on. |
Rate Limit | This is not in the above list because no rate limit was specified, but it may be configured when you add a site to specify the maximum number of requests per minute: -rateLimit <limit> . Attempts to make requests beyond the limit will result in an error. The limit is specified in terms of the maximum number of requests per minute. If this is not specified, or set to 0, then no limit is imposed. |
To change a site’s properties you simply add it again with all the properties required for the change. Each addition of the same site-key completely overwrites the previously stored entry.
Once the site is setup in Approov (and you’ve waited a couple minutes or so for the configuration to propagate), you can try retrieving an Approov token using a web protection request end-point. A simple async function to perform the request is provided below:
async function fetchApproovToken(hcapToken) {
const params = new URLSearchParams()
params.append('approov-site-key', '<Approov-Site-Key>')
params.append('api', '<Target-Approov-Protected-API-Domain>')
params.append('hcaptcha-site-key', '<hCaptcha-Site-Key>')
params.append('hcaptcha-token', hcapToken)
const response = await fetch('https://web-1.approovr.io/attest', {
method: 'POST',
body: params
})
if (!response.ok) {
throw new Error(response) // reject with a throw on failure
}
return response.text() // return the token on success
}
Note that hcaptcha-ekey
and hcaptcha-host
can also be specified when requesting an Approov token. These parameters are described further in the section on endpoints.
The Approov token should never be logged in production code.
The precise code required to obtain the hCaptcha token and then call the above function will depend on the your preferred approach for using hCaptcha. The key flow for using Approov is to first obtain an hCaptcha token, then pass it to fetchApproovToken
before passing that to your API backend. The simplest example provided for hCaptcha, hCaptcha’s Developer Guide, is to introduce a div
with h-captcha
class inside the form
to be protected:
<form>
...
<div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>"></div>
...
</form>
In this approach, the example HTML doesn’t explicitly retrieve the hCaptcha token that’s obtained, it is just embedded in the form data and submitted from there. To use Approov in the same style, then you need to ensure that the Approov token is also added to a hidden form input field before the form is valid for submission. One approach for this, dervied from the above example, is provided below:
<script>
async function fetchApproovToken(hcapToken) {
// as above...
}
function dataCallback(hcapToken) {
fetchApproovToken(hcapToken)
.then(approovToken => {
const tokenEl = document.getElementById('approovToken')
tokenEl.value = approovToken
enableSubmitCheck()
})
}
function enableSubmitCheck() {
// a function triggered whenever we need to check if the submit button
// should be enabled. Note that this is important, so that we can check
// for the presence of the Approov token before the form is submitted.
let shouldDisable = false
// Other checks here ...
const tokenEl = document.getElementById('approovToken')
if (tokenEl.value == "") {
shouldDisable = true
}
const submitEl = document.getElementById('submit')
if (shouldDisable) {
submitEl.setAttribute('disabled', true)
} else {
submitEl.removeAttribute('disabled')
}
}
</script>
<form>
...
<div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>" data-callback="dataCallback"></div>
...
<input id="approovToken" type="hidden" value="" name="approovToken"></input>
<input id="submit" type="submit" value="Submit" disabled>
</form>
The downside of this approach is that a user action (clicking the Submit
button) is required between the event that retrieves the Approov token and the form submission. This means that the default Approov token lifetime is likely to be too short. Extending the token lifetime is not recommended unless network latency is causing token exipiry before it can be processed by your backend API. The preferred solution is to override the form’s default submit behavior so that the Approov token fetch can be inserted in that flow. This second approach requires that your javascript code construct and issue the request to your API, however, this is also best practice because you can then adjust the request to precisely match the one used by your mobile app (which is highly likely to include some properties in headers, like the Approov token itself).
A modified version of the above, may look something like the example below:
<script>
async function fetchApproovToken(hcapToken) {
// as above...
}
function submitForm() {
const hcapToken = hcaptcha.getResponse()
if (hcapToken != "") {
fetchApproovToken(hcapToken)
.then(approovToken => {
// construct fetch request for API call ...
})
.catch(err => {
// handle token fetch error
})
}
}
</script>
<form action="javascript:submitForm()">
...
<div class="h-captcha" data-sitekey="<hCaptcha-Site-Key>"></div>
...
<input id="submit" type="submit" value="Submit">
</form>
Other examples provided in the hCaptcha documentation can also be adapted in a similar vein to the one described.
embed
token claim for hCaptchaYou may wish to interrogate the hCaptcha results in your backend. hCaptcha properties are added to the embed
claim of the Approov token which is a JSON object with a property called hcap:<SiteKey>
, where the SiteKey is the hCaptcha site-key registered to your Approov account and used in the Approov token request. By default only the challenge timestamp and domain attributes are included in the token (using the same JSON structure as the full hCaptcha result), thus confirming that an hCaptcha lookup occurred. The following example shows the full decoded body of an Approov token with the default properties embedded from a hCaptcha request lookup:
{
"exp": 1620398492,
"ip": "1.2.3.4",
"arc": "2VU2BFPIIO",
"embed": {
"hcap:59a3279b-4fb7-40ed-a1a5-4c7abe0a100c": {
"challenge_ts": "2021-05-07T14:39:15",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
If the -embedResult
flag is used when you register the hCaptcha site and you have chosen to use encrypted tokens, JWEs, for the target API then the full results are included in the token. Note that the results from the hCaptcha lookup vary depending on your subscription level. A slightly abbreviated example is shown below:
{
"exp": 1620398895,
"ip": "1.2.3.4",
"arc": "BIFTVTMS6I",
"embed": {
"hcap:59a3279b-4fb7-40ed-a1a5-4c7abe0a100c": {
"success": true,
"challenge_ts": "2021-05-07T14:45:58",
"hostname": "<domain-of-the-calling-webpage>"
}
}
}
You should now be all setup to generate and receive web protection Approov tokens using hCaptcha. For the full reference on the web protection API endpoints and the more advanced features that are provided, please see the following reference section.
Once your web app is receiving tokens from Approov you can start looking at live and accumulated activity using Approov metrics. A full description of the available features are covered in the associated documentation: Metrics Graphs. That section of the docs lists all the metrics that may be used and describes their meaning. Launch the metrics dashboard with the following CLI command: approov metrics
.
In short, the metrics for web protection based activity are integrated with the dashboards and graphs that also present mobile attestation activity. Live web protection metrics are included in the Live Metrics dashboard and they are prefixed with pass-web
or fail-web
. Web protection metrics are also presented by the Billing Usage, Hourly Metrics, Daily Metrics, and Monthly Metrics dashboards with associated extra prefixes.
This section outlines the full specification of the Approov web protections interface. It presents the domains through which the service can be accessed; the end-point specifications; a description of how to use multiple web protection services in a single request; how to perform token binding; and how to troubleshoot problems.
The web protections service can be accessed from the following domains:
web-1.approovr.io
web-1.approovr.com
Both of these domains point to the same service instances some of which are located in the EU and others in the US.
Two endpoints are provided, /attest
and /attest-multi
. On success, a 200
status is returned. The first returns an Approov token string for a single API. The second returns a JSON object that can contain tokens for multiple APIs; for the case when multiple calls to different APIs are required at the same time. Both endpoints accept the same parameters; /attest-multi
accepts the api
parameter multiple times. The previous examples demonstrate the handling of the single token string returned from /attest
. On success, the /attest-multi
endpoint returns a JSON object, with the same number of entries as there were api
parameters and of the form:
{
"<API-Domain-1>": "<Approov-Token-1>",
"<API-Domain-2>": "<Approov-Token-2>",
...
}
A successful request, may return valid or invalid tokens indicating whether the checks on the associated web protections passed or failed. An error (400
status) is also possible; please see the section on Troubleshooting for a description of the possible reasons.
Both endpoints accept GET
and POST
methods. For GET
requests all parameters must be URL encoded. For POST
requests, parameters may be URL encoded or passed in the request body (as in the examples given previously). Mixing URL encoded parameters and parameters passed in the body is not permitted. Note that, some browsers enforce a maximum length on the URL which may cause some URL encoded requests to fail. hCaptcha in particular warn about this.
Parameter Name | Value Description |
---|---|
approov-site-key |
Specifies the Approov site key to identify your Approov account to the web protection servers. Your Approov site key is listed as the first property output from a call to approov web -list . Only a single instance of this parameter is permitted. |
api |
Specifies the domain of the target API. A single api parameter must be specified for every request. The /attest-multi end-point permits more than a single entry. Each api parameter must identify a new API domain that is registered with your Approov account and has been web enabled. The CLI command, approov api -list will list the registered APIs and their properties, including if they are web enabled. |
payload |
An optional parameter used for token binding on the web channel. Only a single instance of this parameter is permitted. See the section below on Web Protection Token Binding. |
fingerprintjs-token |
Specifies the browser token for a FingerprintJS subscription you have registered with Approov. If this is specified, then there must also be corresponding fingerprintjs-visitor-id and fingerprintjs-request-id parameters added to the request |
fingerprintjs-visitor-id |
Specifies the visitor ID returned by a FingerprintJS browser lookup |
fingerprintjs-request-id |
Specifies the request ID returned by a FingerprintJS browser lookup |
hcaptcha-site-key |
Specifies the Site-Key for an hCaptcha site you have registered with Approov. If this is specified, then there must also be a corresponding hcaptcha-token parameter added to the request. Other hCaptcha parameters may also be added as specified below.Note that we don't currently provide a mechanism for specifying all the available hCaptcha siteverify parameters. |
hcaptcha-token |
Specifies the token returned by an hCaptcha browser lookup |
hcaptcha-ekey |
Specifies the challenge key returned by the hcaptcha.getRespKey() JS API call. This is an optional hCaptcha parameter. If present, there must be corresponding hcaptcha-site-key and hcaptcha-token parameters. See hCaptcha documentation; the next section provides an example showing how the parameter is defined. |
hcaptcha-host |
Specifies the root host domain that must match with the hCaptcha response. If present, there must be corresponding hcaptcha-site-key and hcaptcha-token parameters. See hCaptcha documentation; the next section provides an example showing how the parameter is defined. |
recaptcha-site-key |
Specifies the Site-Key for a reCAPTCHA site you have registered with Approov. If this is specified, then there must also be a corresponding recaptcha-token parameter added to the request |
recaptcha-token |
Specifies the token returned by a reCAPTCHA browser lookup |
Although previous examples all focus on a single wrapped web protection service, the endpoints support the addition of parameters from multiple web protections in a single request. In this case, valid Approov tokens are only provided if the web protection checks pass for all parameters. An example fetchApproovToken
function that uses all the supported web protection services is provided below as an example:
async function fetchApproovToken(visitorId, requestId, hcapToken, recapToken) {
const params = new URLSearchParams()
params.append('approov-site-key', '<Approov-Site-Key>')
params.append('api', '<Target-Approov-Protected-API-Domain>')
params.append('fingerprintjs-token', '<FingerprintJS-Browser-Token>')
params.append('fingerprintjs-visitor-id', visitorId)
params.append('fingerprintjs-request-id', requestId)
params.append('hcaptcha-site-key', '<hCaptcha-Site-Key>')
params.append('hcaptcha-token', hcapToken)
params.append('hcaptcha-ekey', hcaptcha.getRespKey())
params.append('hcaptcha-host', '<Site-Root-Domain>')
params.append('recaptcha-site-key', '<reCAPTCHA-Site-Key>')
params.append('recaptcha-token', recapToken)
const response = await fetch('https://web-1.approovr.io/attest', {
method: 'POST',
body: params
})
if (!response.ok) {
throw new Error(response) // reject with a throw on failure
}
return response.text() // return the token on success
}
Approov Token Binding provides a mechanism for linking an Approov token with other properties that will be sent to you API backend, like a User Authentication token. See the section on Token Binding for the mobile channel for further background.
The web channel has the same restrictions as the mobile channel, the bound data needs to be provided as a Base64URL encoded SHA256 sum. The Approov backend converts this to a standard Base64 value as it is added to the generated Approov token. This ensures that no Personal Identifiable Information(PII) can accidentally make it into the Approov backend services. The web protection endpoints respond with an error, if the payload does not conform to this restriction. The following function can be used to generate a valid digest for an arbitrary string:
async function generatePayloadData(message) {
if (message !== "") {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
const hashArray = new Uint8Array(hashBuffer); // convert buffer to byte array
const hashString = String.fromCharCode.apply(null, hashArray) // convert byte array to String (without char interpretation)
const b64 = btoa(hashString) // convert to b64
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '') // convert to b64url
}
return ""
}
Note that the above function uses the restricted crypto.subtle.digest
function. Browsers only permit calls to this function when the site is served from localhost
or when it is served over https
.
Note also that a Google search for Javascript that generates a Base64URL digest will result in many example code snippets that don’t generate the correct result. If you wish to use an alternative, please check that its output matches that of the above function.
The following snippet shows how to extend a fetchApproovToken
implementation to append a payload argument whenever a non-empty message is provided:
async function generatePayloadData(message) {
// as above...
}
async function fetchApproovToken(..., message) {
const params = new URLSearchParams()
params.append('approov-site-key', '<Approov-Site-Key>')
params.append('api', '<Target-Approov-Protected-API-Domain>')
params.append(...)
const payload = generatePayloadData(message)
if (payload !== "") {
params.append('payload', payload)
}
const response = await fetch('https://web-1.approovr.io/attest', {
method: 'POST',
body: params
})
if (!response.ok) {
throw new Error(response) // reject with a throw on failure
}
return response.text() // return the token on success
}
The Approov web protection endpoints normally respond with valid or invalid Approov tokens. However, if there is a problem with a request that prevents the normal web protection handler from completing then an error response will be generated. Also, if the underlying calls to the integrated protection APIs fail with an error, then this error is propagated to the response instead of generating failed tokens.
In these cases a 400
status and an error JSON object is returned from the endpoint. The JSON object is a sequence of key-value pairs, with the key indicating the type of error and the value providing extra information where appropriate. For some error types the value is just the empty string:
{
"<error-code-1>": "<further-details>",
"<error-code-2>": "",
...
}
An attempt is made to batch all problems with a request into the single error object to help make debugging quicker, however, this isn’t always possible. The following table lists the error keys with a description of their cause. Any extra data provided in the associated value may aid debugging.
Error Code | Description | |
---|---|---|
rqst-body-read-fail |
There was a problem reading the request body. | |
rqst-params-in-body-and-path |
The request contains parameters in the URL and in the message body; this is not permitted. | |
rqst-body-too-large |
The request message body is too large. | |
rqst-body-parse |
The request message body could not be decoded; it's probably not formatted correctly. | |
rqst-illegal-params |
The request included illegal paramters; the list of illegal entries is included in the value. | |
aprv-no-site-key |
Your Approov Site-Key was not included in the request parameters. | |
aprv-no-web-protect-params |
The request didn't contain any web protection parameters; no parameters for FingerprintJS, hCaptcha, or reCAPTCHA. | |
aprv-no-matching-account |
The specified Approov Site-Key didn't match any account. | |
aprv-no-web-protect-def |
The specified Approov Site-Key doesn't have any web protection integrations defined. | |
aprv-payload-param-invalid-size |
The request has a payload parameter, but it has an invalid size (32 bytes when decoded). It must be a Base64URL encoded SHA256 hash, see Web Protection Token Binding. |
|
aprv-payload-not-b64url |
The request has a payload parameter, but it isn't Base64URL encoded. It must be a Base64URL encoded SHA256 hash, see Web Protection Token Binding. |
|
aprv-api-no-targets |
The request parameters don't include any api target. |
|
aprv-api-empty-target |
The request parameters include an api target whose value is the empty string. |
|
aprv-api-duplicate-target |
The request parameters include more than one api entry with the same value. |
|
aprv-api-too-many-targets |
The request parameters include more api targets than there are APIs defined for the target account. |
|
aprv-api-not-found |
One or more api targets are not defined in your Approov account. |
|
aprv-api-not-web-enabled |
One or more api targets have not been web enabled. |
|
fpjs-config-not-found |
FingerprintJS parameters were used in the request but no FingerprintJS subscriptions are defined for the account. | |
fpjs-missing-required-params |
The request doesn't have FingerprintJS parameters, but a FingerprintJS check is required by the account, see the section on FingerprintJS configuration. | |
fpjs-param-mismatch |
Some of the parameters for a FingerprintJS check are specified but others are missing. | |
fpjs-empty-browser-tok |
A request with FingerprintJS parameters has the browser token parameter set to the empty string. | |
fpjs-empty-vis-id |
A request with FingerprintJS parameters has the visitor ID parameter set to the empty string. | |
fpjs-empty-rqst-id |
A request with FingerprintJS parameters has the request ID parameter set to the empty string. | |
fpjs-browser-tok-not-found |
The FingerprintJS Browser Token specified in a request doesn't match any defined in the selected Approov account. | |
fpjs-tok-check |
There was an error performing the FingerprintJS server-side check; the error string is included in the value. | |
hcap-config-not-found |
hCaptcha parameters were used in the request but no hCaptcha Sites are defined for the account. | |
hcap-missing-required-params |
The request doesn't have hCaptcha parameters, but an hCaptcha check is required by the account settings, see the section on hCaptcha configuration. | |
hcap-param-mismatch |
Some of the parameters for an hCaptcha check are specified but others are missing. | |
hcap-empty-site-key |
A request has an hCaptcha site key parameter set to the empty string. | |
hcap-empty-token |
A request has an hCaptcha token parameter set to the empty string. | |
hcap-site-key-not-found |
The hCaptcha Site key specified in a request doesn't match any defined in the selected Approov account. | |
hcap-token-check |
There was an error performing the hCaptcha server-side check; the error string is included in the value. | |
recap-config-not-found |
reCAPTCHA parameters were used in the request but no reCAPTCHA Sites are defined for the account. | |
recap-missing-required-params |
The request doesn't have reCAPTCHA parameters, but a reCAPTCHA check is required by the account settings, see the section on reCAPTCHA configuration. | |
recap-param-mismatch |
Some of the parameters for a reCAPTCHA check are specified but others are missing. | |
recap-empty-site-key |
A request has a reCAPTCHA site key parameter set to the empty string. | |
recap-empty-token |
A request has a reCAPTCHA token parameter set to the empty string. | |
recap-site-key-not-found |
The reCAPTCHA Site key specified in a request doesn't match any defined in the selected Approov account. | |
recap-token-check |
There was an error performing the reCAPTCHA server-side check; the error string is included in the value. |
When an Approov account is created it is allocated a random secret key that is used by default for Approov token signing or encryption. This provides Approov tokens signed using the HS256
algorithm (or A256GCMKW
if an encrypted JWE is selected). The HS256
verification uses the symmetric 512-bit account secret key, which needs to be made available to the backend API, to check the validity of the token.
Key sets allow additional keys to be added to an Approov account, which can then be used to sign or encrypt Approov tokens for specific API domains. This allows the use of different keys for different API domains if required. Moreover, key sets also allow access to a much richer set of signing algorithms. In particular, asymmetric signing algorthms (such as RS256
) allow the validity of a token to be verified using only the public key. This avoids the security risks associated with needing to add a symmetric key to the API backend (such as with HS256
), which could allow valid Approov tokens to be generated if it were to be compromised.
A new, randomly generated, keyset key can be added as follows. You can specify the name which you wish to give the key and the algorithm used for signing and encryption, as follows:
$ approov keyset -kid mykey -add RS256
successfully added key
In this case a new key named mykey
is generated for use with the RS256
signing algorithm. A random RSA
private key is generated by Approov and added to your account.
The following signing algorithm are available:
HS256
, HS384
, HS512
: These HMAC signing methods use a symmetric key, with differing hashing algorithms. If you use these then you must be careful to secure the symmetric key used to verify them, since it can also be used to create fresh valid tokens.RS256
, RS384
, RS512
: These use RSASSA-PKCS1-v1_5
asymmetric cryptography, with differing hashing algorithms. The Approov tokens are signed with the private key which can be verified in the backend API using the associated public key.PS256
, PS384
, PS512
: These use RSASSA-PSS
asymmetric cryptography, with differing hashing algorithms. The Approov tokens are signed with the private key which can be verified in the backend API using the associated public key. The difference from RSASSA-PKCS1-v1_5
is that there is a random element to the signature, which may be advantageous to differentiate Approov tokens if many are being generated with the same expiry timestamp.ES256
, ES384
, ES512
: These use ECDSA
asymmetric cryptography, with differing hashing algorithms. ES256
uses curve P-256
(secp256r1
), ES384
uses P-384
(secp384r1
), and ES512
uses P-521
(secp521r1
). The Approov tokens are signed with the private key which can be verified in the backend API using the associated public key.A256GCMKW
: This is the only supported encryption algorithm to generate a JWE
rather than a JWS
. This uses a symmetric key, so if you use this then you must be careful to secure the symmetric key since not only can it be used to decrypt and verify tokens, it can also be used to encrypt fresh valid tokens.This article provides a good overview of the different options.
Once a new keyset key has been added, it can be referenced by the -keySetKID
option for Keyset Key API Addition.
It is also possible to specify a key length override parameter when specifying an algorithm that uses RSA (RS256
, RS384
, RS512
, PS256
, PS384
, PS512
) or symmetric keys (HS256
, HS384
, HS512
, A256GCMKW
).
$ approov keyset -add RS256 -keyLength 4096
successfully added key
The default length for an RSA key is 2048 bits, but it may be overridden to 1024 or 4096 bits. The default length for a symmetric key is 256 bits, but may be overridden to 128 or 512 bits.
Note also that in this case no kid
parameter was specified. If this is not provided then the key is named using a simple incrementing counter for the account.
The existing Quickstart Backend Integrations are written assuming the default signing algorithm of HS256
and that the account secret key is being used. You will need to make small alterations to these to support different algorithms and the different manner in which public keys are obtained for keyset keys.
It is only possible to have a maximum of 10 keyset keys at any one time.
The current keys in the keyset can be listed as follows:
$ approov keyset -list
1 key:
mykey alg:RS256 apis:mydomain.com
The algorithm associated with each key is provided, along with a list of any apis
that the key is currently associated with for token signing. A given key may be associated with multiple different API domains.
A keyset key can be removed as follows:
$ approov keyset -kid mykey -remove
key mykey removed
Note that you need an admin role to remove a key from the keyset. Furthermore, you can only remove a key if it is not in use on a particular API domain. If so, then you need to Remove the API Domains first before the key can be deleted. This needs to be done with care as it will have an immediate impact on your production usage.
It is possible to obtain the PEM file for a specific key within the keyset. This is the encoded form of the public key for the key specified by the -kid
option, in PKIX ASN.1 DER
format. The encoded public key is a SubjectPublicKeyInfo
structure, as defined in RFC5280, section 4.1. Obtain the PEM
as follows:
$ approov keyset -kid mykey -getPEM mykey.pem
$ less mykey.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQU...
-----END PUBLIC KEY-----
Various external tools and API gateways support this format to allow the checking of Approov tokens.
It is only possible to obtain a PEM for RSA
and ECDSA
key types, that use asymmetric cryptography. The command generates a PEM containing only the public key. This enables token verification but not token creation which means public disclosure of the PEM file does not enable a path to unauthenticated access to your API. It does however mean that external parties can determine the validity of Approov tokens which is undesirable. Depending on your security posture and if this does happen, you may still want to change the token secret for the associated APIs.
It is possible to obtain the JSON Web Key Set for all keys in the keyset as follows:
$ approov keyset -getJWKS keys.jwks
$ less keys.jwks
{
"keys": [
{
"alg": "RS256",
"e": "AQAB",
"kid": "mykey",
"kty": "RSA",
"n": "4NhUHZSl5ws8...",
"use": "sig"
}
]
}
Note that you need an admin role to get the JWKS
for the key set. This is because this will also obtain any symmetric keys in the key set, and these require rigorous security since they allow creation as well as verification of Approov tokens. For asymmetric RSA
and ECDSA
keys only the public key is obtained.
It is possible to import a JSON Web Key into the keyset as follows:
$ less byok.jwk
{
"alg": "RS256",
"use": "sig",
"d": "FumDxNCs...",
"dp": "mUxVEADBN...",
"dq": "Yv9xjIj5vOo...",
"e": "AQAB",
"kid": "byok",
"kty": "RSA",
"n": "05yGbvzgIobbtaeJ...",
"p": "9_NNlMTz_S5jcxORGz...",
"q": "2ns2LBc5-1f3m7iI...",
"qi": "TxLIVbqDsTvmhTgmR..."
}
$ approov keyset -import byok.jwk
private key imported
A kid
claim must be present and not be the same as any existing key in the key set. The alg
and use
claims must also be present and be consistent with algorithms supported by Approov, or else the import request will be rejected.
Note that you need an admin role to import a private key. For an asymmetric key, once the private key is imported it is not possible to export it again. Only the corresponding public key may be exported. Extreme care must be taken to secure the file holding the JWK
since it contains the private key and can thus be used to create valid Approov tokens.
If you have multiple Approov accounts, then the importing feature allows you to Bring Your Own Key (BYOK) and share them between multiple accounts. This allows Approov tokens generated by different accounts to use the same Approov protected backend API using a common key. In this case the private key should be generated externally to Approov and then imported into the required accounts. Alternatively, you could make the backend API compatible with multiple different key IDs that it can lookup from the token header)
This is an advanced option that enables a method in the SDK to sign arbitrary messages, using HMAC with the SHA-256 algorithm. This ensures strict message integrity between the client app and the backend API, and is also an additional proof that the requests are truly emananting from an app that has passed the Approov attestation process. Message integrity is also assured by Public Key Pinnng, which we strongly recommend that you implement (all of our frontend integration quickstarts do this). We provide message signing as an advanced option for certain situations where an additional level of integrity assurance is required.
When message signing is enabled the Approov account is assigned a random 512-bit secret that is used for the signing process. This secret key is only transmitted to the SDK if it passes attestation and is provided with a valid Approov token. If the attestation fails, and an incorrectly signed Approov token is sent, then an incorrect message signing secret is also provided so that the app’s message signatures will be incorrect.
The client app should concatenate the relevant parts of a request into a single string. This should then be passed to the signing method in the SDK to obtain the signature. The signature should then be added to the request, typically as an additional http
header, before the request is issued. It should always be submitted in addition to the Approov token. Moroever, you should always include the Approov token itself as part of the string submitted for signing as this strongly binds the Approov token to the signature and prevents replay attacks (since the Approov token has a short expiration time). The backend server is able to gather the components of the request and build the same single string as the client and calculate the expected signature, knowing the secret message signing key being used. This should match the submitted signature, or else the message has been tampered with in some way during transit or the originator of the message did not have access to the correct secret key.
A recent RFC provides an outline of the issues with implementing signing on http
requests. A consistent approach must be used between the client app and the server about which particular headers to be included, and in a consistent order. Moroever, networking intermediaries may subtly transform the message between the client app and the server in semantically valid ways, but this may mean that the message is no longer bitwise equivalent and message signatures fail. To avoid this, care must be taken to transform elements of the request to some canonical form prior to the signing process on both the client and the server.
Even if you are using message signing to verify requests, you should also pass the Approov token. A check should be made on the validity of the Approov token before checking the message signature. A check on the message signature should only be performed if there is a valid unexpired Approov token with a mskid
claim matching the known message signing secret. If there is a valid token without an mskid
then the request should be treated as legitimate. The reason for this is that the Approov failover system does not include mskid
claims in the tokens it generates and so message signatures cannot be generated (or checked) when the failover is active.
Message signing can be enabled on the account as follows:
$ approov secret -messageSigningKey change
WARNING: changing the message signing key will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
key ID: EXTnGx
encoding: base64
RVhUbkd4b01iQ1N3R3Y2cGM4dVdsTEU1UG14S3VOTVhkVDk1dWstb0xPVFZtSEZkdVRueW1lYmRUZWFmR1U3SQ==
An admin role is required. All further Approov token fetches will also transmit the signing secret to the SDK and the key ID for it will appear in the mskid
claim in Approov tokens. The generated message signing secret is provided in base64 format. It can also be obtained in other formats.
The command can also be used to change the message signing secret to a new random value if required. Remember that the new secret must be made available to backend systems to do the signature verification.
The current message siging key can be obtained as follows:
$ approov secret -messageSigningKey get-raw
key ID: EXTnGx
encoding: raw
EXTnGxoMbCSwGv6pc8uWlLE5PmxKuNMXdT95uk-oLOTVmHFduTnymebdTeafGU7I
An admin role is required. This example uses get-raw
to get the raw secret value which is always 64 characters long. The generated secret only consists of printable characters, to support this.
Note that the key ID, which is included in the mskid
claim of Approov tokens, is composed of the first few characters of the raw secret.
You can also obtain a base64 or base64url encoded form of the secret key with get-base64
or get-base64url
.
Message signing can be disabled on the account as follows:
$ approov secret -messageSigningKey clear
WARNING: changing the message signing key will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
message signing key cleared
An admin role is required. Once disabled, the Approov cloud service will stop adding the mskid
claim to new Approov tokens. Furthermore, once running apps receive their next token, the signing secret will be cleared and the message signing method in the SDK will no longer generate signatures.
Prior to going into production you may wish to check the impact of clearing the message signing key, and that this continues to allow backend API operation as expected. This emulates the behaviour expected should the Approov cloud system automatically transition to failover operation.
A method is provided in the SDK that is able to generate the HMAC signature of a given message (interpreted in UTF8 format). This should only be called after the Approov SDK has been initialized and an Approov token has been successfully fetched:
// generate an HMAC signature of the message
String signature = Approov.getMessageSignature(message);
if (signature != null) {
<add the signature as a header>
}
// generate an HMAC signature of the message
let signature = Approov.getMessageSignature(message)
if signature != nil {
<add the signature as a header>
}
The message
should be constructed from the concatenation of the parts of the request which should be subject to signing. Make sure that the approach is identical between the apps and the backend so that a consistent message will be constructed, and canonicalized forms are used for any parts of the message subject to legitimate modification by intermediate hops in the communication. Note that we strongly recommend that you always include the Approov token as part of the message.
The result is a base64 encoding of the 256-bit HMAC result. This should be added as an additional header, alongside the Approov token in the request. If the SDK is unable to sign the message then no header should be added. There are a number of possible reasons for this:
Message signatures are only supported in version 2.5.0 and later SDKs.
It is important that this message signature checking is only performed in the backend as a step after having checked the Approov token in the normal way, i.e. a correctly signed and unexpired Approov token must be present (that will also be included in the signed part of the request).
A check should then be made to ensure that the Approov token includes a mskid
claim. The message siganture should not be checked if this is not present, to allow correct operation with the Approov failover. You should also verify that the key ID matches one held by the backend.
The following provides an example of how the message signature might then be checked in the backend.
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(rawSigningSecret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKey);
String calculatedSignature = Base64.encodeToString(sha256_HMAC.doFinal(message.getBytes()), Base64.NO_WRAP);
if (!calculatedSignature.equals(providedSignature)) {
// <Replace this block with the request rejection code>
Log.e(TAG, "Message signatures do not match, provided=" + providedSignature + ", calculated=" + calculatedSignature);
}
}
catch (NoSuchAlgorithmException e) {
Log.e(TAG, "No HMAC support");
}
catch (InvalidKeyException e) {
Log.e(TAG, "Invalid secret key");
}
The inputs to this process are as follows:
rawSigningSecret
: The message signing secret obtained from the Approov CLI.providedSignature
: The base64 signature as provided on an incoming header. The absence of this header should be considered equivalent to a signature mismatch.message
: The overall message gathered from the request, in an ordering consistent with that used by the client app.Obviously a real example would likely reject traffic with invalid signatures rather than simply log it.
It is not necessary to enforce signature checking on all API endpoints. You may wish to only apply it on particularly security sensitive ones. Of course, there must be consistency between the app and the backend API in these choices.
Approov for Android provides a facility to detect if the app is being launched in an automated way, rather than via the standard launch icon on the device. For instance, this detects if the app is being launched from Android Studio, via an adb shell am start
sequence or via monkeyrunner. Typically you would not expect these launches from a production app, and therefore such an automated launch may well be associated with activity that you wish to block. Automated ad clicking commonly uses farms of devices launched and controlled in an automated fashion.
Devices on which automated launching is detected still receive valid Approov tokens (assuming there are no other detected causes for rejection). This is because they still represent untampered instances of the real app running in a standard environment. However, you may optionally select the Annotation Policy of info
to force automated launch properties to appear in the annotation of the Approov token.
In order to detect an automated launch it is necessary to provide the Approov SDK with a reference to the Activity. This should be done in the onCreate
method of any starting activity for the app. Starting activities are denoted in the AndroidManifest.xml
with the appropriate intent filters, for example:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
A simple call to the Approov SDK as follows notifies it of the Activity. It performs analysis on it to determine if there is evidence of an automated activity start. Note that setActivity
cannot be called until after the Approov SDK is initialized, but it is assumed that this will be done in the onCreate
method of the Application so it will have been completed first.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Approov.setActivity(this);
Your backend integration can detect the presence of the properties in the anno
claim and react accordingly to requests from the device. The possible properties are as follows:
non-standard-launch
: Indicates that an Android app is being launched in some non standard way. This might simply be due to the use of a custom launcher app on a consumer’s device, but may be used as a risk indicator along with other factors for automated usage.automated-launch
: Indicates that an Android app is being launched in some automated way rather than from the standard launcher. For a production app this is a strong indicator of automation that may be associated with some nefarious use of the app.Metrics graphs provide both live and longer term summary information about your account usage. They can be used to see the total number of devices that have requested Approov tokens over a time period (and therefore what the usage related costs will be) and also show the failures where particular devices have been denied valid Approov tokens. The graphs provide information about the reasons for any such failures.
There are two ways that you can reach the metrics graphs:
(1) Directly via the link that may have been provided in your onboarding email.
(2) The metrics portal can also be reached via an approov
command. This automatically opens the metrics information in your browser:
$ approov metrics
opening https://metrics.approovr.com/redirect/test-account/usage?access_token=eyJhbGciOiJ…
The URL being opened is shown in the command line and a new browser window is opened with the relevant information. If the browser does not open for some reason (perhaps because there is no default browser is setup) then the given URL can be pasted in another browser window to view the information.
Note that the browser request or onboarding link contains an access token to authorize viewing of the information. Since this is pasted into a browser, it should be handled with care. It is a specially constructed token that only provides read-only access to the metrics information and no other aspects of the account. If you are ever concerned that this link may have been compromised then you can use the command approov monitoring -refreshMetricsLink
and all previous links will be revoked and a new one generated. Note that this same link is shared between all users of the account.
The metrics graphs are rendered using Grafana. Please refer to Grafana Getting Started to understand how to use the basic features of Grafana. Note that you will be provided with a fixed set of dashboards showing information from your account and will not be able to edit these dashboards or add new data sources.
A number of different individual dashboards are available for your account, each showing different information and described in subsequent sections. Each dashboard may contain a number of different graphs showing active data from your account. Individual dashboards may be selected using the pull-down menu on the top left hand side of the screen next to the Grafana logo.
Graphs are rendered over a timescale defined in the text box at the top right of the screen. If you click on this then various timescale options are provided. Moreover you can click the refresh button to the right hand side of this initiate a refresh to show the latest data. Options for automatic refresh are also available. Note that times are always shown in UTC, regardless of your local timezone setting. The timescale settings impact all graphs shown on the same dashboard. It is possible to zoom into a graph simply by left clicking on the graph and zooming into the area of interest.
Each graph shows a number of time series, depending upon the graph and the exact circumstances of your account. On many graphs there are numerous time series so it may be difficult to see an individual one. You can restrict the graph to only show a single time series by clicking on it in the legend on the right hand side of the graph. Clicking on it again enables all time series. You can use the control key to perform a multi-select to show an arbitrary set of time series together.
The legend shows the maximum value in the time range being shown. For some graphs an average is also available. Hovering over the graph allows the values to be seen on each selected time series at an arbitrary time.
The authentication method used for Approov Grafana dashboards does not allow the “Share” features to be used. If you wish to share the dashboards you can copy and share the URL generated by the approov metrics
command, or in your onboarding email.
The graphs provide access to a Configuration button which lists available Plugins. Note that these are not available for use in your account.
The Approov cloud service generates a number of individual metrics for your account. These represent different activities that are occurring in your account and help you see an overview of usage and any potential issues. There are a number of different types of metrics that are described in the following sections.
Summary metrics are used to provide an overview of the status of a particular attestation request received from an app associated with your Approov account.
Metric Name | Description |
pass |
Indicates that a valid Approov token was issued in response to a mobile attestation. |
fail |
Indicates that an invalid Approov token was issued in response to a mobile attestation because a criteria was not met, depending upon the rejection policy selected. Other metrics will provide information about the reason for the rejection. |
fail-bad-request |
Indicates that a request was rejected outright in response to a mobile attestation because it was incorrectly formed. Under normal circumstances this should not occur, but could indicate a failed attempt to spoof requests for the account to the Approov servers. |
pass-web |
Indicates that a valid Approov token was issued in response to a web protection request. |
fail-web |
Indicates that an invalid Approov token was issued in response to a web protection request because a criteria was not met, depending on the configuration for your web protection services. |
error-web |
Indicates that an error response was issued in response to a web protection request. This may indicate genuine errors received by your web app or a failed attempt to spoof requests. Error details are not available from Grafana, see the Troubleshooting Web Protection Errors section if this represents responses to legitimate requests. |
Flag metrics provide information about the presence of particular characteristics of devices attesting using your account. These correspond to device property flags that have been extracted from the running device with a prefix flag-
added. Furthermore, whenever these metrics are shown they are either prefixed with pass-
or fail-
depending on whether the overall request was passed or failed.
Whether a given request is rejected or not will depend on the Rejection Policy associated with the account or requesting device. This means that if the security policy is changed then flags previously associated with passes may become fails (or vice versa).
These are metrics generated depending on the app making the request. This allows an easy assessment of the population of apps, or those associated with a particular problematic behaviour. Whenever these metrics are shown they are either prefixed with pass-
or fail-
depending on whether the overall request was passed or failed. Whether a given request is rejected or not will depend on the Rejection Policy associated with the account or requesting device. Note that app version metrics are only available in the live metrics.
Metric Name | Description |
app-<os>-<name>
|
The package name of the app that is requesting an Approov token. The app is running on the given os , either ios or and for Android.
|
appver-<os>-<name>-<version>
|
The package name of the app with its corresponding app version that is requesting an Approov token. The app is running on the given os , either ios or and for Android. This allows tracking of specific versions and the transition to a new version when it is pushed out into the app store.
|
These are metrics generated from statistics collected in each running Approov SDK, which are then aggregated across your entire account. These allow an assessment to be made of the rate of usage of the SDK methods and the prevalence of any errors generated.
Metric Name | Description |
sdk-calls-fetch-sync
|
Number of calls to made to the synchronous fetchApproovTokenAndWait method in the SDK.
|
sdk-calls-fetch-async
|
Number of calls to made to the asynchronous fetchApproovToken method in the SDK.
|
sdk-calls-fetch-config
|
Number of calls to made to the fetchConfig method in the SDK.
|
sdk-calls-get-pins
|
Number of calls to made to the getPins method in the SDK.
|
sdk-calls-set-data-hash
|
Number of calls to made to the setDatahashInToken binding method in the SDK.
|
sdk-calls-integrity-proof
|
Number of calls to made to the getIntegrityMeasurementProof method in the SDK.
|
sdk-calls-device-proof
|
Number of calls to made to the getDeviceMeasurementProof method in the SDK.
|
sdk-calls-message-signature
|
Number of calls to made to the getMessageSignature method in the SDK.
|
sdk-result-success
|
Number of token fetch calls resulting in a SUCCESS / success status.
|
sdk-result-no-network
|
Number of token fetch calls resulting in a NO_NETWORK / noNetwork status (assuming the app could subsequently get network access to report this).
|
sdk-result-poor-network
|
Number of token fetch calls resulting in a POOR_NETWORK / poorNetwork status.
|
sdk-result-mitm-detected
|
Number of token fetch calls resulting in a MITM_DETECTED / mitmDetected status.
|
sdk-id-<value>
|
Number of token fetch calls made from the Approov SDK with given value ID.
|
sdk-arch-<platform>-<processor>
|
Number of token fetch calls made from a device with a given processor architecture. The platform may be either ios or android . The processor provides the type of processor used, allowing differentiation between 32-bit and 64-bit systems. Intel architectures for iOS are for the simulator only, whereas Android supports native Intel processor instruction sets.
|
sdk-version-<value>
|
Number of token fetch calls made from the Approov SDK with given value version number. This is suffixed with -bitcode if the SDK is being used in bitcode mode (for iOS).
|
The policy metric is prefixed policy-
with the remainder of the metric name providing the name of the rejection policy that was being to the device. This will typically be the default policy for the account, but may be a device specific policy.
Filter metrics are prefixed filter-
with the remainder of the metric name as defined using the approov filter -add
command. Thus these are user defined labels representing particular matches of filtered values of device attributes in Approov token fetches.
Web protection metrics are generated after integrating a web protection service with one of your web apps; see Web Protection Integration. The metrics are prefixed pass-web-
or fail-web-
as appropriate. The metrics summarise the activity and failure reasons for your integrated web-protections.
Metric Name | Description |
pass-web-fpjs |
A valid Approov token was issued for a web protection request requiring a FingerprintJS check |
pass-web-hcap |
A valid Approov token was issued for a web protection request requiring an hCaptcha check |
pass-web-recap |
A valid Approov token was issued for a web protection request requiring a Google reCAPTCHA check |
fail-web-aprv-rate-limit-exhausted |
An invalid Approov token was issued because a rate limit was triggered for one or more of the integrated web protections |
fail-web-fpjs-vis-or-rqst-id-unknown |
An invalid Approov token was issued because the FingerprintJS lookup failed to recognise the specified VisitorID or RequestID |
fail-web-fpjs-timestamp-too-old |
An invalid Approov token was issued because the timestamp for the FingerprintJS request happened too far into the past. This may indicate a malicious attempt to reuse RequestIDs or that the current Max Elapsed Time setting for the Subscription is too short. See the section on FingerprintJS configuration. |
fail-web-fpjs-bot-probability |
An invalid Approov token was issued because the BotProbability score returned by the FingerprintJS lookup was higher than the configured maximum for the subscription. See the section on FingerprintJS configuration. |
fail-web-hcap-request-invalid |
An invalid Approov token was issued because the hCaptcha lookup responded with the Success property set to false |
fail-web-hcap-domain-not-found |
An invalid Approov token was issued because the hCaptcha lookup responded with a domain that is not in the configured set of acceptable domains. This only triggers if the hCaptcha site in Approov has been configured with a set of domains to check. See the section on hCaptcha configuration. |
fail-web-hcap-bad-score |
An invalid Approov token was issued because the hCaptcha lookup responded with a score that was below the minimum configured for the site. See the section on hCaptcha configuration. |
fail-web-recap-request-invalid |
An invalid Approov token was issued because the reCAPTCHA lookup responded with the Success property set to false |
fail-web-recap-action-not-found |
An invalid Approov token was issued because the reCAPTCHA lookup responded with an action that is not in the configured set of acceptable actions. This only triggers if the reCAPTCHA site in Approov has been configured with a set of actions to check. See the section on reCAPTCHA configuration. |
fail-web-recap-domain-not-found |
An invalid Approov token was issued because the reCAPTCHA lookup responded with a domain that is not in the configured set of acceptable domains. This only triggers if the reCAPTCHA site in Approov has been configured with a set of domains to check. See the section on reCAPTCHA configuration. |
fail-web-recap-bad-score |
An invalid Approov token was issued because the reCAPTCHA lookup responded with a score that was below the minimum configured for the site. See the section on reCAPTCHA configuration. |
The Live Metrics dashboard allows you to see what is happening in an account on a near real time basis. Information shown in the graphs is updated within a maximum of 2 minutes from the event occurring. New data is made available every minute.
The live metrics are shown across four independent graphs:
pass-
indicating this status. The pass
summary metric shows the total number of mobile app attestation passes, the pass-web
summary metric shows the same for passing web protection requests.fail-
indicating this status. The fail
summary metric shows the total number of mobile app attestation failes, the fail-web
summary metric shows the same for failing web protection requests. For mobile attestations, it is important to understand that this shows the properties of the failing device, but these are not necessarily the properties that caused it to fail. Those are shown in the Live Rejections graph. For web protection requests, this graph includes all causes of failure.rejection-
to indicate this. This graph also shows the Policy Metric which is being applied and determines which device properties may lead to a rejection.The y axis for the graphs are in terms of the volume of requests (requests per minute). The same device making multiple requests in a time period will be counted multiple times.
During initial development, when there are no production apps in the account also providing data, the graph can be used for quickly determining if requests are reaching the Approov servers and assessing the response made.
This dashboard provides an overview of the usage of an account over time showing Summary Pass and non-versioned App metrics. A new entry is generated on each hour. For mobile attestation billing, the y axis scale is based on the number of unique device IDs that have been seen since the start of your account’s billing period in the month. For web protection integrations, the y axis shows the accumulated number of web protection requests resulting in valid Approov tokens.
This figure will grow over the month until it is reset on the “billing day” anniversary each month. The maximum value that the monthly-pass
and monthly-pass-web
figures reach during the month is used to calculate the account’s usage invoice for the month. Since billing is calculated only on the basis of passing attestation and web protection requests all metrics shown are in fact prefixed monthly-pass
.
In addition to the overall figure, mobile app attestation metrics are also provided for each app package name that has been successfully used on the account. This allows an assessment to be made between iOS and Android usage, or for different apps associated with the account.
This dashboard provides an overview of the hourly usage of the account, in terms of the number of different device IDs that used the account in an hour period and the total number of web protection requests that were made. A new sample is output every hour. It shows Summary, Flag, App and Web Protection metrics. This allows assessment on the level of account usage from hour to hour in terms of the count of different devices or web protection requests.
The upper graph shows devices and web protection requests that were granted a valid Approov token in the last hour, and metrics are prefixed with hourly-pass
. The lower graph shows the devices and web protection requests that had at least one failure to receive a valid Approov token in the last hour, and metrics are prefixed with hourly-fail
. Pass metrics are restricted to a total and per-app package breakdown. Fail metrics also show flags that are a cause of the failure (i.e. those shown with the rejection-
prefix in the live metrics). Note that if a device both passed and failed in the last hour then it will be counted in both graphs.
This dashboard provides an overview of the daily usage of the account. The number of devices and web protection requests are counted throughout the day, until reset at midnight UTC. A new sample is output every hour. It shows Summary, Flag, App and Web Protection metrics. This allows assessment on the level of account usage from day to day in terms of the count of different devices or web protection requests.
The upper graph shows devices and web protection requests that were granted a valid Approov token in the last day, and metrics are prefixed with daily-pass
. The lower graph shows the devices and web protection requests that had at lest one failure to receive a valid Approov token in the last day, and metrics are prefixed with daily-fail
. Pass metrics are restricted to a total and per-app package breakdown. Fail metrics also show flags that are a cause of the failure (i.e. those shown with the rejection-
prefix in the live metrics). Note that if a device both passed and failed in the last day then it will be counted in both graphs.
This dashboard provids an overview of the monthly usage of the account. The number of devices and web protection requests are counted throughout the month, until reset at midnight UTC on the billing day of the account. A new sample is output every hour. It shows Summary, Flag, App and Web Protection metrics.
The upper graph shows devices and web protection requests that were granted a valid Approov token in the month, and metrics are prefixed with monthly-pass
. The lower graph shows the devices and web protection requests that had at lest one failure to receive a valid Approov token in the month, and metrics are prefixed with monthly-fail
. Pass metrics are restricted to a total and per-app package breakdown. Fail metrics also show flags that are a cause of the failure (i.e. those shown with the rejection-
prefix in the live metrics). Note that if a device both passed and failed in the last month then it will be counted in both graphs.
This dashboard shows live SDK metrics that are being collated from running Approov SDKs in apps associated with the account. These are aggregated across all SDKs with a new sample being generated every minute. These metrics are not directly associated with a pass or fail.
The metrics are shown across three independent graphs:
sdk-result
metrics. These in particular can be used to determine if there are any widespread issues affecting running SDKs.The vertical scale is of succeeding or attempted token fetches. Note that if a particular SDK instance is without network for an extended period while trying to perform fetches then when it reconnects the count of attempts made is associated with that connection time, which may cause a spike in the graph for sdk-result-no-network
or sdk-result-poor-network
results.sdk-calls
metrics. These are related to counted calls made in the SDK, and will typically be larger than the number of actual Approov token fetches. For instance, calls to get an Approov token when one is already cached are counted here.sdk-arch
and sdk-id
metrics. This shows the particular architectures and library IDs that are in use.It is possible to export raw graph data into a CSV file using the menu available on the right click button on each graph, as follows:
Approov provides facilities that allows monitoring of the status of your account. A healthcheck API endpoint is supported, along with monthly or even daily summaries of usage and notification emails if there is a certificate problem with one of your endpoints.
Summary emails can be sent automatically at the end of each billing month. This is typically on the anniversary day within the month of your signup to the Approov service. Typical emails are of this form:
Information provided is as follows:
The email should be received shortly after midnight UTC on your billing day. The email is copied to all those on the recipient list. The next section shows how you may update the recipients. Shortly after receiving the summary email you will also receive an invoice or automated credit card charge reflecting the usage shown in the summary email.
The Approov cloud service is able to monitor your API endpoints to ensure that they are both accessible and that the certificates presented match one or more of the pins you have set in your account. The endpoints are checked every 15 minutes, and a notification email is sent if a problem is detected. This will list each of the monitored API domains that are experiencing an issue.
When you add an API domain monitoring for it will be activated automatically, unless no pin was added or one was added via local access (implying the API endpoint is not generally visible on the Internet). Note that notifications can only be sent if your account’s email recipients list has been setup with one or more entries.
You can always set a new API for monitoring as follows:
$ approov monitoring -addAPI mydomain.com
API monitoring updated successfully
An admin
role is required. If the API is not accessible on the standard port of 443 you can use the -port
option to select monitoring on a different port.
If an email is sent then, by default, no further ones will be sent for 24 hours. However, you can override this behaviour by issuing the following command:
$ approov monitoring -resumeAPINotifications
API monitoring notifications resumed
You will then receive an email again if there is a further problem after the APIs are checked on the next (or a subsequent) 15 minute cycle.
Monitoring for a specific API domain can be removed as follows:
$ approov monitoring -removeAPI mydomain.com
API monitoring updated successfully
This might be required if the API domain is not accessible from the Approov cloud service, or is not part of your production system and you don’t wish to monitor it continuously.
When your account is first created there are no email addresses allocated for receiving the summary emails. You may add a new recipient as follows:
$ approov monitoring -add ops-monitoring@mydomain.com
Be careful with the addition of email addresses as these are not verified at the time of addition. It is possible to have up to 10 different monitoring email recipients.
You can retrieve the current settings at any point as follows:
$ approov monitoring -get
Producing a response such as:
monitoring emails will be sent to:
me@mydomain.com
ops-monitoring@smydomain.com
summary frequency: monthly
emergency contact: Email at ops@mydomain.com, out of hours cell +1 23456789
Email recipients can be removed at any point with:
$ approov monitoring -remove ops-monitoring@mydoman.com
By default only a monthly summary email will be sent. However, it is also possible to opt into a daily summary email that is also sent soon after midnight UTC. It provides a summary of the passing and failing devices from the day, representing the daily endpoint of the data in Daily Metrics.
Opt into daily emails as follows:
$ approov monitoring -selectDailySummary
You may always return to monthly emails only with:
$ approov monitoring -selectMonthlySummary
A facility is provided to allow you to set an emergency contact. This will allow Approov to contact your operations team in an exceptional circumstance, such as a major Approov outage or if we detect a serious problem with your account. Ideally therefore you should provide some mechanism to allow us to contact your operations team out of hours.
You can specify a string containing contact information with:
$ approov monitoring -setEmergencyContact "Email at ops@mydomain.com, out of hours cell +1 23456789"
Remember you can always contact our operations team using the technical support email support@approov.io
.
The Approov cloud service incorporates an always-on health monitoring system which actively monitors the state of various internal cloud server components, reporting the overall health state and operational readiness of the primary service. This has an externally accessible API for determining the system health for your account. You can retrieve the URL for this endpoint as follows:
$ approov monitoring -getHealthCheckURL
This provides the URL that you should use for checking your particular account health, e.g.
https://healthcheck.approovr.io/healthcheck/att-my-account
Note that it is common for account names to be prefixed with att-
, as shown here. You can query this endpoint to determine the system health as follows:
$ curl -X GET https://healthcheck.approovr.io/healthcheck/att-my-account
This endpoint will return a “200 OK” HTTP response with a JSON body including a HealthState
key with value initializing
, passed
or failed
. You would normally expect:
{
"HealthState": "passed"
}
You can regularly poll this endpoint if you wish to determine the system heath. We do not recommended that you poll more than once a minute. The possible states are as follows:
initializing
: Appears temporarily after a service restarts one or more components and indicates that the system is gathering data to determine the health status. When this operation is complete, the health monitor will report a passed
or failed
health state as appropriate.passed
: Indicates that the system is up and running correctly. All operations can be used as normal.failed
: Indicates that the system has encountered a fault in one or more of its components. The administration APIs may be down which means the service cannot be administered using the approov
command line tool. In the unlikely event that the service is unhealthy, the health monitoring system will report a passed
state as soon as the system becomes healthy again.Note that the endpoint only returns the health of the primary service. If there is a problem with the primary service then the failover system is automatically enabled, as described in Cloud Server Redundancy. This means that even if the primary system is being reported as being unhealthy then it is highly likely that Approov tokens will still be being server by the system. Note that if the health endpoint itself is down then it is not possible to determine the system state. However, since this endpoint is not itself served by the primary system and is independent of it, the likelihood is that it remains fully operational.
It is possible to provide access to your Approov account (via the approov
CLI) to other members of your team.
When a new account is created the account holder is issued with dev
and admin
roles. One the capabilities of the admin
role is the ability to add new users, with specific roles.
In a larger organization where multiple personnel need to interact with the Approov service, we expect that there will be some internal control of the access to Approov. We recommend that only a single individual, or small restricted group, should have access to the admin
role. We suggest that dev
roles are created for each of the individuals that are involved in the development of the apps that use the Approov service. These tokens allow access to all of the facilities required for app development, without more dangerous privileges that might impact apps that are live in production. The dev
user roles can be issued to named individuals, preferably for a timescale that represents their likely involvement. Access can be revoked by the administrator at any time.
For larger and more complex teams and projects we recommend having multiple Approov accounts to provide enhanced insulation between development and production operations. If this is a requirement then please contact Approov support.
There are five different types of role may be used with the approov
command line tool. These are dev
, admin
, delegate
, pentest
and automation
.
An admin
role has additional privileges that are not available for the dev
role as follows:
-removeMatching
option.dev
role as this does not carry a risk of outage.The delegate
role has a more limited set of rights, and it will only ever be required in special circumstances. It is used when a 3rd party (who also has their own Approov account) is responsible for developing and registering the app that will be used to access endpoints controlled by this account. The privileges of the role are as follows:
registration
command with the -cloneTo
, -cloneToAccounts
or -cloneToAllDelegateAccounts
options.approov sdk -getConfigString
or approov sdk -getConfig
commands.approov appsigncert
commands.approov devicecheck
commands.approov safetynet
commands.Note that the delegate role provides no rights to get the account secret key, or keyset keys, associated with the account.
The pentest
role has a very limited set of rights. It is designed to allow an independent pentester of the app to modify the security policy applied to a device. This, for example, allows pinning to be disabled or an Annotation Policy to be set showing the reason for a rejection on a device. The privileges of the role are as follows:
approov policy -get
.approov token -genExample
command, for testing the backend API.The automation
role has a very limited set of rights. It is intended to support automated scripted use of the approov CLI, especially for Continuous Integration (CI) build systems (see Automated Approov CLI Usage.
approov sdk -getLibrary
command.approov sdk -getConfigString
or approov sdk -getConfig
commands.approov registration -add
.approov apis -getAll
, and associated api
and pin
commands that only read the API and pinning state. This also includes approov api -check
.Note if there are any other operations you would like to perform from your build and other automation scripts using an automation
role, then please contact Approov support.
Additional user roles may only be issued if you have access to an admin
role. Here is an example of issuing Approov access to a new user with dev
role privileges:
$ approov users -add bob@mydomain.com
WARNING: you are adding dev role access to account myaccount for bob@mydomain.com (bob)
ATTENTION: If you wish to continue then please type YES and return: YES
onboarding email has been sent to bob@mydomain.com
The -add
option has a parameter of the user email. By default the new user role will have the same duration as the admin
role used to create it. Moreover the new user role will be password protected (unless overridden with the -noPassword
option). The user will receive an Approov Onboarding
email with instructions of how to initialize access and they will be invited to set their password.
By default a dev
user role will be created. This has limited privileges as discussed in the previous section. A new admin
role may be created as follows, in this case with a shortened validity period of one week.
$ approov users -add boss@mydomain.com -admin -expireAfter 7d
WARNING: you are adding admin role access to account myaccount for boss@mydomain.com (boss)
ATTENTION: If you wish to continue then please type YES and return: YES
onboarding email has been sent to boss@mydomain.com
The -expireAfter
parameter will normally be specified in terms of the number of days that access will be available for, but years (y
) or hours (h
) may also be used. Note that it is not possible to generate a user role that has an expiry time after that of the admin
role used to create it.
A user name can also be associated with the role. By default this is derived from the part of the email address before the @
. You can override this using the -userName
option. Quotes must be used around the parameter if you wish to use spaces in the user name.
A new delegate
user role may be created as follows:
$ approov users -add appdev@theirdomain.com -delegate
onboarding email has been sent to appdev@theirdomain.com
The delegate
role can be provided to an independent app developer, who will then be able to use it to clone registrations from their own Approov account.
A new pentest
user role may be created as follows:
$ approov users -add pentester@theirdomain.com -pentest
onboarding email has been sent to pentester@theirdomain.com
The pentest
role can be provided to an independent pentester, which will enable them to control security policies for specific devices only, and to generate example Approov tokens for testing.
Note that there is currently a maximum limit of 25 user roles that may be live for any individual Approov account. If this limit is reached then old unused user roles should be revoked to allow the creation of new ones.
Approov will send access expiry reminder emails to the holder of the user role. These are sent soon after midnight UTC daily for the few days prior to the expiry to remind the user that their access is about to expire. No further messages are sent if access is revoked or reaches its expiration.
Note that you will also receive expiration reminders about your primary user roles provided when you first initialized access to your Approov account. If you have a paid plan then you should receive a new onboarding email from Approov shortly after the reminder emails are issued.
The available user roles can be listed as follows:
$ approov users -list
3 user roles:
admin-5794 me@mydomain.com, A N Other, expires 2029-05-08 15:01:05
dev-0639 me@mydomain.com, A N Other, expires 2029-05-08 15:01:05
dev-1234 bob@mydomain.com, bob, expires 2029-05-08 15:01:05, password protected
This command output provides the information about the active user roles with a unique ID for each. The first part of the ID is the role, which may be dev
, admin
or delegate
. Other information includes the email addresses, user name, expiry date and if password protection is enabled.
In some cases it is desirable to be able to revoke the use of a specific user role before its expiry time. This may be necessary if there is a concern that the access has been compromised or the individual that was provided access has left the project. A user role can be revoked as follows:
$ approov users -revoke dev-1234
WARNING: you are revoking dev-1234 for bob@mydomain.com, bob, expires 2029-05-08 15:01:05
ATTENTION: If you wish to continue then please type YES and return: YES
user access dev-1234 revoked
The user role ID provided as the parameter can be obtained using the -list
option. Revocation can take up to 30s. After revocation completes, attempts to use the role will result in an authorization error, e.g.
$ approov registration -list
ERROR: access is revoked
error: Forbidden, while getting management token status
Note that it is not possible to revoke the user roles issued by Approov when you first signed up. If you feel that your access may have been compromised, then please contact Approov support who are able to revoke these roles and then provide new ones.
It is possible to send another onboarding email to an existing user. This is may be desirable if the user is has lost access to Approov for whatever reason, or they wish to access Approov from a different machine and their original onboarding email has expired. You must select an email address that is associated with one or more roles, and use the following command:
$ approov users -sendOnboardingTo bob@mydomain.com
onboarding email has been sent to bob@mydomain.com
An onboarding email is sent to the user which will give them 24 hours to install access to Approov on to the machine(s) of their choosing.
You may wish to use the Approov CLI from within automated scripts, perhaps on remote runner machines for Continuous Integration (CI) systems. In this case the interative approov role
mechanism may not be appropriate, since it requires a user to type in the password and the session only lasts for one hour. Thus a mechanism is provided whereby an automation
role management token can be defined in a local environment variable to provide Approov account access. This will not be password protected.
An automation
role management token has limited rights, but should be sufficient for the operations required in a build environment.
Management tokens must be considered to be sensitive secrets since they allow access to your Approov account. Take care with security when setting these up. Never log them and never commit them to source code repositories.
You should create an automation
management token for the purposes of the scripted access. An email address must be provided, for the user that is the custodian of the management token. We suggest you override the user name with information about the intended purpose, or the machine that the management token is going to be used on. For instance:
$ approov users -add me@mydomain.com -userName "CI Machine" -automation
WARNING: you are adding automation role access to account myaccount for me@mydomain.com (CI Machine)
ATTENTION: If you wish to continue then please type YES and return: YES
automation management token written to automation-1234.tok
set this in an APPROOV_MANAGEMENT_TOKEN environment variable on the machine that requires it
This writes the automation management token to the file automation-1234.tok
, in this case. Follow the instructions for Linux, MacOS or Windows to set the APPROOV_MANAGEMENT_TOKEN
environment variable.
If you have multiple different build machines then you could consider having a differently named automation
managament token for each one. They can then be independently revoked if required. Moreover, any app registrations made will be tagged with the management token name.
If you have also performed an approov init
for the same user that is using an automation
management token set with APPROOV_MANAGEMENT_TOKEN
then you will get a warning on approov CLI command invocations. This reminds you that the APPROOV_MANAGEMENT_TOKEN
is usurping the role that would normally be selected. We generally encourage you to not issue approov init
for users or machines that will only be using an automation
token. You can remove initialized roles by removing the .approov
file in the user’s home directory.
You can do this easily with the following command:
$ read -r APPROOV_MANAGEMENT_TOKEN < automation-1234.tok; export APPROOV_MANAGEMENT_TOKEN
The parameter automation-1234.tok
should point to the file containing the development management token that you received on signup. You can test the environment variable was set by issuing an approov whomai
command.
Note that the environment variable APPROOV_MANAGEMENT_TOKEN
will only be set while this shell is running. If you want to make the setting more permanent then you can edit your ~/.bashrc
file (or equivalent for other shells) and add the line:
APPROOV_MANAGEMENT_TOKEN=eyJhbGciOiJIUzI1NiI…
The string for your development token should be copied from the automation-1234.tok
file. Alternatively you can run the read
command above in the context of your shell startup. Now any newly created shells have the management token available in the environment so that it is not necessary to make it available on each approov
command invocation.
You can do this easily with the following command:
$ read -r APPROOV_MANAGEMENT_TOKEN < automation-1234.tok; export APPROOV_MANAGEMENT_TOKEN
The parameter automation-1234.tok
should point to the file containing the development management token that you received on signup. You can test the environment variable was set correctly by issuing an approov whoami
command.
Note that the environment variable APPROOV_MANAGEMENT_TOKEN
will only be set while this shell is running. If you want to make the setting more permanent then you need to edit your shell initialization file. This is ~/.bashrc
for versions prior to Catalina, or ~/.zshrc
since Catalina which uses the zsh
by default. If you are using a non-standard shell then you will need to consult its documentation. Add the line:
export APPROOV_MANAGEMENT_TOKEN=eyJhbGciOiJIUzI1NiI…
The string for your development token should be copied from the automation-1234.tok
file. Alternatively you can run the read
command above in the context of your shell startup. Now any newly created shells have the management token available in the environment so that it is not necessary to make it available on each approov
command invocation.
Open the advanced settings page:
Then click on the “Environment Variables” button. This opens up another dialog where you can click on the New
for user variables to add a new environment variable. The token should be put in the user, rather than system, variables so that it is not accessible to other users of the same machine.
The development token string should be copied from the automation-1234.tok
file.
Command shells created after setting the value and closing the dialog will have the management token available in the environment so that it is not necessary to make it available on each approov
command invocation. You can test that this is correctly setup with an approov whoami
command.
If you have previously created an automation
role management token then you can retrieve its value again as follows. Firstly, list the user roles to find the role ID value (on the left hand side of the listing):
$ approov users -list
...
automation-1234 me@mydomain, CI Machine, expires 2021-12-04 11:47:11
...
You can then obtain the management token itself using the role ID:
$ approov users -getManagementToken automation-1234
management token written to automation-1234.tok
This section discusses the Approov feature that allows the SDK to prove to a remote hardware device, not directly connected to the Internet, that it is a genuine instance of a particular app.
This feature is useful for specific use cases where the mobile device and/or some other remote hardware being commanded by an app may not have a continuous Internet connection. Approov attests itself in the normal way and accesses an API, but additional step for this flow causes a measurement of the app integrity to be taken. This is included in some communication from the backend API to the remote hardware. This will include an encrypted form of the measurement that only the remote hardware is able to decrypt. The remote hardware is then able to know the value of the prior measurement made on the app. An offline attestation process between the app and the remote hardware then proves that the app is able to reproduce the measurement without actually transmitting it. This gives assurity to the remote hardware that it is being commanded by the same app that was earlier measured. This prevents an attacker from commanding the remove hardware using any other client software.
Note that this feature must be enabled in the Approov cloud service before it can be used from an app. Contact Approov support if you are not sure of the status or wish to perform a trial of the feature. It is not provided in our standard Approov commercial package.
The following provides an overview of the steps required to obtain and use a measurement proof using the offline security feature. The first stage is the app obtaining a measurement:
?measurement
. The fetched Approov token will contain additional information for the collected measurements. The domain must have encrypted JWE tokens enabled, so that the measurements cannot be read by any 3rd party if they are somehow able to intercept the request.The next step (which may be repeated an arbitrary number of times) is to perform a measurement proof between the app and a remote hardware device:
getIntegrityMeasurementProof
and getDeviceMeasurementProof
) are provided in the Approov SDK to allow a measurement proof to be made, salted by a nonce value provided by the app. This should be different each time, to prevent replay attacks. The measurement proof methods require the MC file that was saved earlier. Since this is held in persistent local storage the app can be closed, and indeed the mobile device rebooted, between measurements and the same result will be obtained if no tampering of the app has occurred. Crucially, this entire measurement proof process does not require any Internet access.In order to support offline mode there are some requirements on the backend integration for the API domain that is responsible for issuing measurement tokens to the remote hardware. A key requirement is that some Public Key Infrastructure (PKI) needs to be in place with the remote hardware. This means that the backend API can encrypt the measurement token and only the specific remote hardware (or class of remote hardware) should be able to decrypt the contents. Of course this depends on keeping the private key in the remote hardware secure, but this is beyond the scope of Approov.
The backend API must check the Approov token in the normal way, but then extract the imh
(Integrity Measurement Hash), dmh
(Device Measurement Hash) and mpk
(Measurement Proof Key) out of the Approov token and put the values into the measurement token.
Encrypted JWEs must be used on endpoints to which measurements are transmitted for additional security.
It is envisaged that the app will communicate with a remote hardware in order to command it in some manner. This will require a combination of a valid measurement proof and likely other user credentials to be accepted by the remote hardware. In other words, it is checking that it is being commanded by the right software and by the right user. The exact channel of communication is independent of the Approov feature, but a typical choice is Bluetooth.
A valid measurement proof indicates that the Approov SDK is able to reproduce the integrity measurement value that was calculated earlier when the measurement was first obtained. Each measurement is salted with a 128-bit nonce value to prevent replays of prior results. A measurement is actually an HMAC of the measurement salted by the nonce. The method of choosing this nonce is not defined by Approov.
The remote hardware implementation must do the following:
HMAC(MPK, nonce || IM)
. The Measurement Proof Key (MPK) is dissolved in the obfuscated code of Approov and provides a further layer of security. The result is a 256-bit value called the Integrity Measurement Proof (IMP). This IMP value is transmitted to the remote hardware and it must perform exactly the same HMAC calculation. Note that a Device Measurement Proof (DMP) can be calculated in exactly the same manner.As discussed previously, the first stage in making an Approov measurement is to request a token in measurement mode. Example code to do this is as follows:
Approov.TokenFetchResult approovResult = Approov.fetchApproovTokenAndWait("api.myservice.io?measurement");
if (approovResult.isConfigChanged())
saveApproovConfigUpdate();
let approovResult = Approov.fetchTokenAndWait("api.myservice.io?measurement");
if approovResult.isConfigChanged {
saveApproovConfigUpdate()
}
This is a standard token fetch call, except for the ?measurement
suffix of the provided domain. This forces a measurement to occur, even if there is a previously cached and valid Approov token. The expectation is the special measurement token will be sent to the API domain api.myservice.io
.
Next, the measurement configuration obtained from this measurement must be saved to local app storage. This code provides an example approach:
if (approovResults.getStatus() == Approov.TokenFetchStatus.SUCCESS)
// check that we obtained a configuration measurement (this should always be the case
// but we check in case there has been an error)
byte[] measurementConfig = approovResults.getMeasurementConfig();
if (measurementConfig == null) {
<error handling>
}
// save the measurement configuration to local storage
try {
FileOutputStream outputStream = getApplicationContext().openFileOutput("measurement_config.bin", Context.MODE_PRIVATE);
outputStream.write(measurementConfig);
outputStream.close();
} catch (IOException e) {
<error handling>
}
}
if approovResult.status == ApproovTokenFetchStatus.success {
// check that we obtained a configuration measurement (this should always be the case
// but we check in case there has been an error)
if approovResult.measurementConfig == nil {
<error handling>
}
// save the measurement configuration to local storage
let URLs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let measurementConfigURL = URLs[0].appendingPathComponent("measurement_config.bin")
do {
try approovResult.measurementConfig!.write(to: measurementConfigURL)
} catch {
<error handling>
}
}
This saves the configuration to the local file measurement_config.bin
. A saved measurement configuration is required in order to perform subsequent proofs that the integrity measurement has not changed. By saving the measurement configuration the app is able to perform such checks even if the app is restarted.
At some point later, a measurement proof may be required to communicate the app’s authenticity to a remote hardware. First, the measurement configuration must be loaded from local app storage again. Example code is as follows:
// read the measurement configuration from local storage
byte[] measurementConfig = null;
try {
InputStream stream = getApplicationContext().openFileInput("measurement_config.bin");
DataInputStream reader = new DataInputStream(stream);
measurementConfig = new byte[stream.available()];
reader.readFully(measurementConfig);
reader.close();
} catch (IOException e) {
<error handling>
}
// read the measurement configuration from local storage
let URLs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let measurementConfigURL = URLs[0].appendingPathComponent("measurement_config.bin")
var measurementConfig : Data? = nil;
do {
measurementConfig = try Data.init(contentsOf: measurementConfigURL)
} catch {
<error handling>
}
Once the measurement configuration is available, a measurement proof calculation can be performed as follows:
// perform the integrity measurement proof calculation
byte[] integrityProof = Approov.getIntegrityMeasurementProof(proofNonce, measurementConfig);
if (integrityProof == null) {
<error handling>
}
// perform the integrity measurement proof calculation
let integrityProof = Approov.getIntegrityMeasurementProof(proofNonce, measurementConfig!)
if integrityProof == nil {
<error handling>
}
This generates a 32 byte result in the integrityProof
variable. The result of this is a combination of the measurement taken earlier when the Approov token was fetched, and a 16-byte proofNonce
value provided by the application. The choice of the nonce is beyond the scope of this document, but it should be varied on each measurement proof.
The measurement proof value can then be transmitted to the remote hardware, along with any other relevant credentials as part of any command from the app to the remote hardware The hardware is able to evaluate this to determine if the app has calculated the same integrity measurement value as previously. Since a non-replayable HMAC proof value is being transmitted, any attacker able to steal data from the channel will not be able to infer the actual integrity measurement value or replay the data later.
If any of the characteristics of the app’s integrity changes, then the computed value will differ and access will be denied. This occurs if there is any change to the app itself, or if the runtime environment has changed. For instance if the app is now under debug, or subject to analysis by a framework, then the measurement will be altered. The measurement may also change if the operating system version of the device on which the app is installed is upgraded.
Note that the Approov SDK also includes a getDeviceMeasurementProof
method. This is used in exactly the same way, but only measures attributes of the mobile device on which the app is running. Thus this calculation will not be impacted by any update to the app (or device’s OS) that is being attested.