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.

The SafetyNet Attestation API provides a cryptographically-signed attestation, assessing the device's integrity. In order to create the attestation, the API examines the device's software and hardware environment, looking for integrity issues, and comparing it with the reference data for approved Android devices. The generated attestation is bound to the nonce that the caller app provides. The attestation also contains a generation timestamp and metadata about the requesting app.

The API is not designed to fulfill the following use cases:

  • Act as a stand-alone anti-abuse or app-security mechanism. Please use it in combination with the published best practices for app security and your suite of product-specific anti-abuse signals.
  • Function when the device isn't connected to the internet. The API returns an error in such scenarios.
  • Have its response interpreted directly in the calling app. Move all anti-abuse decision logic to a server under your control.
  • Provide fine-grained signals about system modifications. The API offers boolean values that express different levels of system integrity.
  • Contain signals for app-specific use-cases, such as device identifiers, GPS emulation status, and screen lock status.
  • Replace or implement strong DRM checks.
  • Purely to check whether the device is rooted, as the API is designed to check the overall integrity of the device.

Overview

The SafetyNet Attestation API uses the following workflow:

  1. The SafetyNet Attestation API receives a call from your app. This call includes a nonce.
  2. The SafetyNet Attestation service evaluates the runtime environment and requests a signed attestation of the assessment results from Google's servers.
  3. Google's servers send the signed attestation to the SafetyNet Attestation service on the device.
  4. The SafetyNet Attestation service returns this signed attestation to your app.
  5. Your app forwards the signed attestation to your server.
  6. This server validates the response and uses it for anti-abuse decisions. Your server communicates its findings to your app.

A graphical depiction of this process appears in Figure 1:

Figure 1. SafetyNet Attestation API protocol

Note: Additional documentation and checklist

Throughout the initialization, configuration and activation of the SafetyNet Attestation API and in addition to this main documentation, be aware of and adhere to the following advice:

Obtain an API key

In order to call the methods of the SafetyNet Attestation API, you must use an API key. The SafetyNet Attestation API is deprecated and so you can no longer request a new API key.

API quota and monitoring

The default quota allotment per project for calling the SafetyNet Attestation API is 10,000 requests per day across your user base. To make a higher volume of integrity requests, migrate to the Play Integrity API and request increased quota based on the instructions in the Play Integrity API documentation. Note that quota requests take a few business days to be processed, so it is recommended to set up quota monitoring and alerting to avoid emergency situations.

Note: Regardless of the quota provisioned for your project, individual app instances are throttled to a maximum of 5 requests per minute. If this limit is exceeded, all remaining requests during that minute return an error.

Keep this behavior in mind when implementing your app's retry mechanism.

Check the Google Play services version

Before you use the SafetyNet Attestation API, you must ensure that the correct version of Google Play services is installed on the user's device. If an incorrect version is installed, your app might stop responding after calling the API. If your app detects that an incorrect version is installed, you should ask the user to update the Google Play services app on their device.

To check whether the installed version of Google Play services is compatible with the version of the Android SDK you're using, call the isGooglePlayServicesAvailable() method, as shown in the following code snippet:

if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
        == ConnectionResult.SUCCESS) {
  // The SafetyNet Attestation API is available.
} else {
  // Prompt user to update Google Play services.
}

On devices running Google Play Services v13.0 and above, the SafetyNet Attestation API also supports app-restricted API keys. This feature reduces the risk of accidental or unauthorized usage of quota-restricted API keys. To use this optional feature, check that the minimum version of Google Play Services on the device is at least v13.0, as shown in the following code snippet:

if (GoogleApiAvailability.getInstance()
    .isGooglePlayServicesAvailable(context, 13000000) ==
    ConnectionResult.SUCCESS) {
  // The SafetyNet Attestation API is available.
} else {
  // Prompt user to update Google Play Services.
}

Request a SafetyNet attestation

After you obtain an API key that is valid for the Android Device Verification API in the Google APIs Console, your app can use the SafetyNet Attestation service. To do so, complete the following steps:

  1. Obtain a nonce.
  2. Request a SafetyNet attestation.
  3. Transfer the response to your server.
  4. Use the response on your server, along with your other anti-abuse signals, to control your app's behavior.

To keep your app responsive, execute these steps outside of your app's main execution thread. To learn more about how to create separate execution threads, see Sending operations to multiple threads.

You should perform this check to protect all critical actions—including logins, purchase events and acquisition of new in-app products—in your app. Calls to the SafetyNet Attestation API incur increased latency, mobile data usage, and battery usage, however, so it makes sense to find a balance between security and usability. As an example, you might choose to request a SafetyNet attestation upon login and run re-checks at most once every 30 minutes. You can also let your server decide when your app requests an attestation, to make it harder for adversaries to predict the timing of your check.

Obtain a nonce

When calling the SafetyNet Attestation API, you must pass in a nonce. The resulting attestation contains this nonce, allowing you to determine that the attestation belongs to your API call and isn't replayed by an attacker.

A nonce used with a SafetyNet request should be at least 16 bytes in length. You should introduce variability in your nonce, ensuring that the same nonce is never used twice. As a best practice, derive part of the nonce from the data being sent to your servers. For example, concatenate the hash of the username with the request timestamp to form the nonce.

Important: Include as many pieces of data in the nonce as possible. In doing so, you make it more difficult for attackers to carry out replay attacks. For example, deriving the nonce from the username limits replay attacks to the same account. However, deriving the nonce from all the details of a purchase event limits the use of the API's response message to that purchase event alone.

Upon receiving the signed response from the API, always compare the nonce in the signed response with the one you reconstruct from the rest of the message sent to your servers. This check ensures that attackers cannot reuse signed attestations harvested from good devices for other, maliciously-crafted requests.

For additional information on using cryptography functions, see the guide on how to use cryptography.

Request the attestation

After you have established a connection to Google Play services and have created a nonce, you're ready to make a SafetyNet attestation request. The response to your request may not be immediate, so it's best to set up a callback listener to handle the response from the service. An example listener appears in the following code snippet:

Kotlin

SafetyNet.getClient(this).attest(nonce, API_KEY)
    .addOnSuccessListener(this) {
        // Indicates communication with the service was successful.
        // Use response.getJwsResult() to get the result data.
    }
    .addOnFailureListener(this) { e ->
        // An error occurred while communicating with the service.
        if (e is ApiException) {
            // An error with the Google Play services API contains some
            // additional details.
            val apiException = e as ApiException

            // You can retrieve the status code using the
            // apiException.statusCode property.
        } else {
            // A different, unknown type of error occurred.
            Log.d(FragmentActivity.TAG, "Error: " + e.message)
        }
    }

Java

// The nonce should be at least 16 bytes in length.
// You must generate the value of API_KEY in the Google APIs dashboard.
SafetyNet.getClient(this).attest(nonce, API_KEY)
    .addOnSuccessListener(this,
        new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
            @Override
            public void onSuccess(SafetyNetApi.AttestationResponse response) {
                // Indicates communication with the service was successful.
                // Use response.getJwsResult() to get the result data.
            }
        })
    .addOnFailureListener(this, new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {
            // An error occurred while communicating with the service.
            if (e instanceof ApiException) {
                // An error with the Google Play services API contains some
                // additional details.
                ApiException apiException = (ApiException) e;
                // You can retrieve the status code using the
                // apiException.getStatusCode() method.
            } else {
                // A different, unknown type of error occurred.
                Log.d(TAG, "Error: " + e.getMessage());
            }
        }
    });

The onSuccess() method indicates that communication with the service was successful, but it doesn't indicate whether the device has passed the SafetyNet attestation. The next section discusses how to read the attestation result and verify its integrity.

Transfer the SafetyNet attestation response to your server

When your app communicates with SafetyNet, the service provides a response containing the result of the SafetyNet attestation and includes additional information to help you verify the integrity of the message. The result is provided as a SafetyNetApi.AttestationResponse object. Use this object's getJwsResult() method to obtain the data of the request. The response is formatted as a JSON Web Signature (JWS).

Send the JWS object back to your server for validation and use.

Use the SafetyNet attestation response on your server

The following JWS excerpt shows the format and sample contents of the payload data:

{
  "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",
  "deprecationInformation": "..."
}

A signed attestation's payload typically contains the following fields:

Timestamp of the response

  • timestampMs: Milliseconds past the UNIX epoch when the JWS response message was generated by Google's servers.

Data provided by the calling app

  • nonce: The single-use token that the calling app passes to the API.

Data about the calling app

  • apkPackageName: The calling app's package name.
  • apkCertificateDigestSha256: Base-64 encoded representation(s) of the SHA-256 hash of the calling app's signing certificate(s)

Integrity verdict

  • ctsProfileMatch: A stricter verdict of device integrity. If the value of ctsProfileMatch is true, then the profile of the device running your app matches the profile of a device that has passed Android compatibility testing and has been approved as a Google-certified Android device.
  • basicIntegrity: A more lenient verdict of device integrity. If only the value of basicIntegrity is true, then the device running your app likely wasn't tampered with. However, the device hasn't necessarily passed Android compatibility testing.

    For more information about Android compatibility testing, see Design an Android Device and Android Compatibility and Compatibility Testing Suite (CTS).

Deprecation Information

  • deprecationInformation: A string which contains information for developers about the deprecation of the SafetyNet Attestation API.

Optional fields

  • error: Encoded error information relevant to the current API request.
  • advice: A suggestion for how to get a device back into a good state.
  • evaluationType: Types of measurements that contributed to the current API response.

Potential integrity verdicts

The JWS message contains two parameters that indicate the result of the check for device compatibility: ctsProfileMatch and basicIntegrity. The status of the device running your app could affect the value for each parameter, as shown in Table 1:

Table 1. Examples of how device status could affect the values of basicIntegrity and ctsProfileMatch

Device Status Value of ctsProfileMatch Value of basicIntegrity
Certified, genuine device that passes CTS true true
Certified device with unlocked bootloader false true
Genuine but uncertified device, such as when the manufacturer doesn't apply for certification false true
Device with custom ROM (not rooted) false true
Emulator false false
No device (such as a protocol emulating script) false false
Signs of system integrity compromise, one of which may be rooting false false
Signs of other active attacks, such as API hooking false false

Error cases

The JWS message can also show several types of error conditions:

  • A null result indicates that the call to the service didn't complete successfully.
  • An error parameter in the JWS indicates that an issue occurred, such as a network error or an error that an attacker feigned. Most errors are transient and should be absent if you make another call to the service. You might want to retry a few more times with increasing delays between each retry.
  • If the device is tampered—that is, if basicIntegrity is set to false in the response—the verdict might not contain data about the calling app, such as the apkPackageName and apkCertificateDigestSha256. This occurs when our systems cannot reliably determine the calling app.

What to do when the signed attestation reports an error?

  • Retry. Errors on legitimate devices are temporary and should go away if you make another call to the service.
  • Check that your app doesn't call the API more than 5 times per minute on the affected devices and that your project's API quota hasn't been exhausted yet.
  • Assume that it might be an attacker intentionally triggering an error case to masquerade their activity.

Advice for passing future checks

When present, the advice parameter provides information to help explain why the SafetyNet Attestation API set either ctsProfileMatch or basicIntegrity to false in a particular result. The parameter's value contains a list of strings, such as the ones in the following example:

{"advice": "LOCK_BOOTLOADER,RESTORE_TO_FACTORY_ROM"}

In your app, you can translate the values in the advice parameter into user-friendly messages to help the user pass future SafetyNet attestations, as shown in the following list:

LOCK_BOOTLOADER
The user should lock their device's bootloader.
RESTORE_TO_FACTORY_ROM
The user should restore their device to a clean factory ROM.

Evaluation types

The evaluationType parameter is present whenever the ctsProfileMatch and basicIntegrity verdicts are present.

The parameter provides information about the types of measurements used to compute fields like ctsProfileMatch and basicIntegrity for a particular response. The parameter’s value contains a list of strings, like in the following example:

{"evaluationType": "BASIC,HARDWARE_BACKED"}

Currently, the possible values are:

BASIC
Typical measurements and reference data were used.
HARDWARE_BACKED
Hardware-backed security features were used. This includes features such as Key Attestation, which is supported on devices that shipped with Android 8.0 (API level 26) and higher.

In your app, you can treat the presence of HARDWARE_BACKED in the evaluationType parameter as an indicator of a stronger device integrity evaluation.

Note: Relying on the evaluationType parameter is recommended only for apps that already use the ctsProfileMatch verdict and which require the highest level of device integrity guarantees, even at the cost of limiting their user base. In most cases, you should continue to rely on the basicIntegrity and ctsProfileMatch verdicts to detect abuse. These verdicts already incorporate hardware-backed security features, where applicable.

If you decide to depend on the presence of a certain value in the evaluationType parameter, then you should consider implementing a retry mechanism in your app in case there are temporary errors.

Verify the SafetyNet attestation response

You should take steps to make sure that the SafetyNet attestation response actually came from the SafetyNet service and includes data matching your request.

To verify the origin of the JWS message, complete the following steps:

  1. Extract the SSL certificate chain from the JWS message.
  2. Validate the SSL certificate chain and use SSL hostname matching to verify that the leaf certificate was issued to the hostname attest.android.com.
  3. Use the certificate to verify the signature of the JWS message.
  4. Check the data of the JWS message to make sure it matches the data within your original request. In particular, make sure that the timestamp has been validated and that the nonce, package name, and hashes of the app's signing certificate(s) match the expected values.

You should verify the JWS statement using standard cryptographic solutions, such as the ones found in the android-play-safetynet sample API usage, available on GitHub.

During initial testing and development (but not in production), you can call an online API for verifying the signature of the JWS statement. This process has also been shown in the android-play-safetynet sample API usage made available on GitHub. Note that online verification API is solely for early-stage testing, and you have a fixed quota of 10,000 requests per day.

Important: The use of the online verification API only validates that the JWS message was signed by the SafetyNet Attestation API's servers. This online API cannot verify whether the fields in the payload match the values that your service expects.

Plan for unexpected cases

We recommend planning your usage so that it takes changes and outages into account.

API changes
New (experimental) fields may appear in the verdict any time. Make sure these extra fields don't break your parser or usage logic. In particular, don't rely on experimental fields before they are announced on the SafetyNet API clients mailing list.
The SafetyNet Attestation API is down

In the unlikely event of the SafetyNet Attestation API being unavailable, users of this API are strongly recommended to build server-side capabilities to dynamically control the dependence on the availability as well as quality of this API and its response.

Typical strategies should include the ability to dynamically instruct your apps to stop calling this API, as well as device- and user-based allowlists to ignore the SafetyNet Attestation API results for certain classes of devices and users.

Sample Code

For additional guidance on working with the SafetyNet APIs, view the sample code that is available on GitHub.

Announcement List

We strongly recommend joining the SafetyNet API clients mailing list to receive updates about the SafetyNet Attestation API.

Feedback

Please consider providing your feedback for this API. We use your feedback to prioritize new features and capabilities for this API.

Learn more

To learn more about the best-practices when using the SafetyNet Attestation API, see the following links:

Additional terms of service

By accessing or using the SafetyNet APIs, you agree to the Google APIs Terms of Service, and to these Additional Terms. Please read and understand all applicable terms and policies before accessing the APIs.

SafetyNet Terms of Service

As with any data collected in large volume from in-the-field observation, there is a chance of both false positives and false negatives. We are presenting the data to the best of our understanding. We extensively test our detection mechanisms to ensure accuracy, and we are committed to improving those methods over time to ensure they continue to remain accurate.

You agree to comply with all applicable law, regulation, and third party rights (including without limitation laws regarding the import or export of data or software, privacy, and local laws). You will not use the APIs to encourage or promote illegal activity or violation of third party rights. You will not violate any other terms of service with Google (or its affiliates).

You acknowledge and understand that the SafetyNet API works by collecting hardware and software information, such as device and application data and the results of SafetyNet attestations, and sending that data to Google for analysis. Pursuant to Section 3(d) of the Google APIs Terms of Service, you agree that if you use the APIs that it is your responsibility to provide any necessary notices or consents for the collection and sharing of this data with Google.

SafetyNet Attestation Data Safety

Google Play has a data safety section for developers to disclose their apps' data collection, sharing, and security practices. To help you complete the data safety section requirements, you can use the following information regarding how the SafetyNet Attestation API handles data:

Type of practice How the SafetyNet Attestation API applies the practice
Data collected on usage
  • Package name
  • Application signing certificate
  • A device attestation token generated by Google Play services
Purpose of data collection The data collected is used to verify the application integrity and the device integrity.
Data encryption Data is not encrypted.
Data sharing Data is not transferred to any third parties.
Data deletion Data is deleted following a fixed retention period.

To complete your data disclosure, you can use Android's guide about data types to help you determine which data types best describe the collected data. In your data disclosure, make sure to also account for how your specific app shares and uses the collected data.

While we aim to be as transparent as possible in supporting you, we can't speak for you, and you are solely responsible for deciding how to respond to Google Play's data safety section form regarding your app's user data collection, sharing, and security practices.