10 things you might be doing wrong when using the SafetyNet Attestation API
Posted by Oscar Rodriguez, Partner Developer Advocate
[Note: This blog was amended in March 2018 to include our latest recommendations regarding app integrity verification, and in May 2019 to amend the quota increase request form link.]
The
SafetyNet Attestation API helps you assess the security and compatibility of
the Android environments in which your apps run. Since it was introduced in
March 2015, many developers have successfully integrated it into their Android
apps to make more informed decisions based on the integrity and compatibility of
the devices running their apps.
Throughout the years, the SafetyNet Attestation API has evolved, and its
adoption has steadily increased. However, as with any security/anti-abuse
related API, there are many common pitfalls that may lead developers into
developing unstable systems, or worse, into a false sense of security.
In this post, we provide a list of the most common mistakes we have seen
developers make when integrating the SafetyNet Attestation API.
1. Not getting an API key
Just like many other Google APIs, the SafetyNet Attestation API requires an API
key in order to run. Furthermore, the SafetyNet Attestation API has a per-key
usage quota. Although you can get this quota increased, you need to provide your
API key to do so.
Getting an API key is easy and free of charge. There is no reason not to get an
API key, so if you haven't already, href="https://developer.android.com/training/safetynet/attestation.html#add-api-key">get
an API key now.
2. Not using the latest version of the API
The SafetyNet Attestation API has evolved throughout its history, and with it,
there have been some interface changes. Most recently, with the release of
Google Play services 11.0.0, we revamped the entire SafetyNet API to offer an
interface that is easier and more streamlined to use: the new API uses href="https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetClient">SafetyNetClient
instead of href="https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetApi">SafetyNetApi
,
which is now deprecated, so make sure you update your implementation to use the
latest version of the API.
Most devices should have the latest version of Google Play services installed,
but if a device doesn't have Google Play services installed, or doesn't have it
up to date, using the SafetyNet Attestation API may lead to the app becoming
unresponsive or crashing. You can prevent this by href="https://developer.android.com/training/safetynet/attestation.html#check-gps-version">checking
the installed version of Google Play services before using the API.
3. Using nonces incorrectly
The SafetyNet Attestation API lets you set a nonce to uniquely and globally
identify each call to the API. Use this feature to prevent a malicious user from
reusing a successful attestation result in place of an unsuccessful result (also
known as a Replay
Attack).
href="https://developer.android.com/training/safetynet/attestation.html#single-use-token">One
good way to create a nonce is to create a large (16 bytes or longer) random
number on your server using a cryptographically-secure random function. The
SafetyNet attestation response includes the nonce you set, so make sure you
verify that the returned nonce matches the one you included in the request you
made.
4. Not checking the results on your server
SafetyNet can provide useful signals about the state of the device in which your
app is running. However, if the logic that acts on these signals is only
implemented and enforced directly on the device, an attacker could be able to
modify your app and bypass any checks you perform.
To prevent this situation, you should run any logic that verifies the
attestation result and enforces any actions based on them on a server that you
control and trust.
5. Using the test attestation verification service for production
In order to simplify development and testing of the SafetyNet Attestation API,
Google offers an href="https://developer.android.com/training/safetynet/attestation.html#verify-compat-check">online
verification service that checks the digital signature of a SafetyNet
Attestation result using a simple HTTPS request.
As useful as this service may seem, it is designed for test purposes only, and
it has very strict usage quotas that will not be increased upon request.
Instead, you should implement the digital signature verification logic on your
server in a way that it doesn't depend on Google's servers. Most JWT libraries
offer signature verification functionality, and we have href="https://github.com/googlesamples/android-play-safetynet">code samples
that show how to perform this verification in Java and C#. We plan to provide
samples for more languages in the future.
6. Not checking the nonce, timestamp, APK name, and hashes
The SafetyNet Attestation API is most widely known for its integrity and
compatibility checks, whose results are returned in ctsProfileMatch
and basicIntegrity
. Although these two values are indeed very
useful, you should check the other values in the response, as they contain
important information as well.
Use nonce
to match a response to its request,
as explained above, and use timestampMs
to check how much time has passed since you made the
request and you got the response. A delayed response that arrives several hours
or days after the request may suggest suspicious activity.
Use apkPackageName
to check the name of the APK that made the
attestation request, and match apkDigestSha256
andapkCertificateDigestSha256
to those from your app's signed APK in
Google Play, to get a signal about the integrity of the installed app.
You can also use apkDigestSha256
to check the hash value of the APK installed on the user device. Be aware, however, that distribution channels, such as Google Play may include additional metadata into the APK, so apkDigestSha256
might differ from the hash value of the APK that you uploaded to your distribution channel. In most cases, using apkPackageName
and apkCertificateDigestSha256
should be sufficient signals for verifying the integrity of the APK.
Remember that the trustworthiness of the response as a whole is tied to the
results of ctsProfileMatch
and basicIntegrity
. It is
not unthinkable for a compromised device that fails basicIntegrity
to have forged the rest of the values in the response.
7. Not understanding the differences between ctsProfileMatch
and basicIntegrity
The SafetyNet Attestation API initially provided a single value calledbasicIntegrity
to help developers determine the integrity of a
device. As the API evolved, we introduced a new, stricter check whose results
appear in a value called ctsProfileMatch
, which allows developers
to more finely evaluate the devices on which their app is running.
In broad terms, basicIntegrity
gives you a signal about the general
integrity of the device and its API. Rooted devices failbasicIntegrity
, as do emulators, virtual devices, and devices with
signs of tampering, such as API hooks.
On the other hand, ctsProfileMatch
gives you a much stricter signal
about the compatibility of the device. Only unmodified devices that
have been certified by Google can pass ctsProfileMatch
. Devices
that will fail ctsProfileMatch
include the following:
- Devices that fail
basicIntegrity
- Devices with an unlocked bootloader
- Devices with a custom system image (custom ROM)
- Devices for which the manufactured didn't apply for, or pass, Google
certification - Devices with a system image built directly from the href="https://source.android.com/">Android Open Source Program source files
- Devices with a system image distributed as part of a beta or developer
preview program (including the href="https://www.google.com/android/beta">Android Beta Program)
8. Not having a strategy for timing attestation checks
The SafetyNet Attestation API gives you a snapshot of the state of a device at
the moment when the attestation request was made. A successful attestation
doesn't necessarily mean that the device would have passed attestation in the
past, or that it will in the future.
Because an attestation is just a spot check, you should plan a sensible strategy
for choosing when to make attestation requests. You may choose to require
successful attestations before users make in-app purchases, after a certain
number of days have passed since the last successful attestation, each time your
app is launched, after every reboot, or any other strategy that makes sense for
your app.
Keep in mind that an attestation request is computationally expensive, consumes
battery and bandwidth, and uses your quota. We recommend you plan a strategy to
use the least amount of attestations required to satisfy your use case.
9. Using the SafetyNet Attestation API results as the only signal
to attack abuse
It may be tempting to think that the SafetyNet Attestation API provides all the
necessary signals for protecting an app against abusers, and use it as the only
signal for building an anti-abuse system.
The SafetyNet Attestation API can only give signals about the state of
a device, not the intent of a user, which is what an anti-abuse system
should be designed to detect. Therefore, you might want to consider including
other signals, such as access logs and behavioral patterns, to more accurately
detect abusive users, and consider not blocking users solely on a failed
attestation. Furthermore, there are many other conditions that cause an
attestation to fail, such as network connection problems, quota issues, and
other transient problems.
In other words, not all users who fail attestation are necessarily abusers, and
not all abusers will necessarily fail attestation. By blocking users solely on
their attestation results, you might be missing abusive users that don't fail
attestations. Furthermore, you might also be blocking legitimate, loyal
customers who fail attestations for reasons other than abuse.
10. Not monitoring and managing your usage quota
As mentioned before, href="https://developer.android.com/training/safetynet/attestation.html#add-api-key">the
SafetyNet Attestation API is rate limited, and there is a default quota of
10,000 requests per day for each API key. Although this quota might be enough
for most development, testing, and initial app launches, your app might reach
the default limit as it increases in popularity.
To prevent inadvertently reaching your quota and getting attestation errors, you
should build a system that monitors your usage of the API and warns you well
before you reach your quota so you can get it increased. You should also be
prepared to handle attestation failures because of an exceeded quota and avoid
blocking all your users in this situation.
If you are close to reaching your quota, or expect a short-term spike that may
lead you to exceed your quota, you can submit
this form to request short or long-term increases to the quota for your API
key. This process, as well as the additional quota, is free of charge.
Komentar
Posting Komentar