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.

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.

In the sidebar on the left, click Credentials. Copy the API key that
appears.

Use this API key when you call the attest()
method of the SafetyNetClient class.

Wait for a confirmation email that indicates when your request has been
processed. You can usually expect your request to be handled within 2-3 business
days.

After getting your increased API quota provisioned, set up quota monitoring and
alerting. This step helps you become aware
that you're running out of API quota, which could happen as your service's use
of the SafetyNet Attestation API grows over time.

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.

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:

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:

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:

Obtain a nonce.

Request a SafetyNet attestation.

Transfer the response to your server.

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.

Note: The SafetyNet Attestation API uses network resources, so the latency
between a request and its corresponding response varies depending on the
device's network connection status.

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.

Note: The preceding code snippet uses a non-blocking version of the attest()
method. If you're making a blocking call instead, always set a response timeout.
That way, your app remains responsive, even if a slow network connection causes
a delayed response from the SafetyNet Attestation API.

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.

Caution: You should send the entire JWS response to your own server, using a
secure connection, for verification. We don't recommend that you perform the
verification directly in your app. By verifying directly in your app, you don't
have any protection against attackers who may remove the verification logic from
the modified version of your app, or may obstruct it from working in
suitably-modified runtime environments.

Use the SafetyNet attestation response on your server

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

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.

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.

Note: If you don't need the device running your app to pass CTS, use the
basicIntegrity parameter, as it allows your app to run on a wider variety
of devices. If your app requires stronger device integrity guarantees, you
should use only the result from ctsProfileMatch. For more information, see
the potential integrity verdicts section.Caution: You should trust the APK information only if the value of
ctsProfileMatch is true.

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.

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 second 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.

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:

Extract the SSL certificate chain from the JWS message.

Validate the SSL certificate chain and use SSL hostname matching to verify
that the leaf certificate was issued to the hostname attest.android.com.

Use the certificate to verify the signature of the JWS message.

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.

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 whitelists 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.

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.

Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.