Among the different mobile platforms available, in this post, we will talk about Android and iOS. Both of these platforms provide their specific implementations and tools to validate their app’s integrity, which means to verify that your servers are interacting with the original and unmodified version of the apps. In this post, you’ll get an overview of the methods used by these two platforms to establish the integrity of their apps. These methods are:
- For Android - Google SafetyNet
- For iOS v13 and below - Device Check
- For iOS v14 and above - App Attestation
Let’s start with Android.
Android - SafetyNet Attestation API
The SafetyNet Attestation API is an anti-abuse API that allows app developers to assess the Android device their app is running on. The API should be used as a part of your abuse detection system to help determine whether your servers are interacting with your genuine app running on a genuine Android device.
How it works?
- Android app interacts with Google SafetyNet Attestation API to construct an attestation token formatted in JSON Web Signature (JWS).
- The SafetyNet Attestation service evaluates the runtime environment and requests a signed attestation of the assessment results from Google’s servers.
- Google’s servers send the signed attestation to the SafetyNet Attestation service on the device.
- The SafetyNet Attestation service returns this signed attestation to your app.
- This JWS is then sent to a backend service for validation.
- The backend service returns the validation results. And to validate the token, the backend service decodes/parses the JWS received from the client app. It should look like this:
{
"timestampMs": 9860437986543,
"nonce": "R2Rra24fVm5xa2Mg",
"apkPackageName": "com.package.name.of.requesting.app",
"apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
certificate used to sign requesting app"],
"ctsProfileMatch": true,
"basicIntegrity": true,
"evaluationType": "BASIC",
}
For a legit Android app running on a legit Android device basicIntegrity
and ctsProfileMatch
should be true
, nounce
should not be empty, and the value of these apkPackageName
and apkCertificateDigestSha256
should match with that of your Android app.
iOS v13 and below - Device Check
The DeviceCheck services consist of both a framework interface that you access from your mobile app and an Apple server interface that you access from your backend service.
Someone who modifies your app and distributes it outside the App Store can add unauthorized features like game cheats, ad removal, or access to premium content. The App attest service gives your app a way to assert its validity so that your server can more confidently provide access to sensitive resources.
How it works?
- iOS app interacts with the App attest service to construct an attestation token base64 encoded.
- The app attest service returns an attestation token to iOS app.
- This token is then sent to a backend service for validation.
- The backend service doesn’t unpack/validate the token and instead forwards this to the Apple Device Check server for validation.
- If the Apple Device Check server returns 200, then it means the Device Check passed. Otherwise, validation failed.
- At this step backend service can device how to proceeed depending on the results received from Apple Server.
A sample request from a backend service to the Apple Device Check server for device validation looks like this.
curl -i --verbose -H "Authorization: Bearer <GeneratedJWT>" \ -X POST --data-binary @ValidValidateDeviceToken Request.json \ https://api.development.devicecheck.apple.com/v1/validate_device_token
Here, the GeneratedJWT
token is generated using the Team Id, Key Id, and the Private Key of your legit iOS app. On how to create this auth token see this: https://help.apple.com/developer-account/#/deva05921840
And Request.json
contains the device token for validation along with some other information.
{
"device_token" : "wlkCDA2Hy/CfMqVAShslBAR/0sAiuRIUm5jQg0aJJ2gZl1cs...",
"transaction_id" : "5b737ca6-a4c7-488e-b928-8452960c4be9",
"timestamp" : 1487716472000
}
iOS v14 and above - App Attestation
For iOS v14 and above the implementation is a multi-step back and forth communication between an iOS app, a backend service, and the Apple App attest service to construct and validate the attestation token.
Every time your app needs to communicate attestation data to your server, the app first asks the server for a unique, one-time challenge. App Attest integrates this challenge into the objects that it provides, and that your app sends back to your server for validation. This makes it harder for an attacker to implement a replay attack.
How it works?
- The iOS app interacts with Apple App attest service to get a key identifier.
- The iOS app then asks its backend service for the first challenge string.
- The backend service generates the first challenge, saves it in a database record, and returns to the app.
- The iOS app uses this challenge and the key identifier received in step 1 to fetch an
attestation
token from App attest service. - The iOS app then sends this
attestation
token and key identifier to the backend service for validation. - The backend service decodes/parses the
attestation
token to validate. It should look like this:
{
"fmt": "apple-appattest",
"attStmt": {
"x5c": [
["<Buffer 30 82 02 cc ... >"],
["<Buffer 30 82 02 36 ... >"]
],
"receipt": ["<Buffer 30 80 06 09 ... >"]
},
"authData": ["<Buffer 21 c9 9e 00 ... >"]
}
- If validation passes on the backend, then the service saves the CredCert (first buffer in the decoded “x5c” arrays) value and another newly generated second challenge string into the database record.
- The backend service sends this second challenge back to the iOS app.
- The iOS app uses this second challenge to generate an
assertion
token from App attest service corresponding to a request body. - The iOS app then sends this
assertion
token in the successive API requests to the backend for validation. - The backend service decodes/parses the
assertion
token to validate. It should look this:
{
"signature": ["<Buffer 30 45 02 20 ... >"],
"authenticatorData": ["<Buffer 21 c9 9e 00 ... >"]
}
- The backend service validates this
assertion
token before proceeding to execute the API call.
Resources
- SafetyNet Attestation API | Android
- DeviceCheck | iOS 13 and below
- App Attestation | iOS 14 and above | Server Side
- App Attestation | iOS 14 and above | Mobile Side
- DeviceCheck-AppAttest An open source library to refer server side implementation of iOS app attestation.