Android SafetyNet API

Communication between a mobile app and any backend server is one of the hardest areas to secure. Secure Sockets Layer or SSL is broken and has been for some time. First things first, if you send data using http then anyone can download your app, proxy the WiFi through a PC and perform man in the middle attacks to see any traffic back to base, see Figure 1. You don’t even need to root the phone.

Basic HTTP

Figure 1: HTTP Traffic

If you use https then your traffic is only marginally more secure. The problem with SSL is that a several of years ago someone stole root certificates from a certificate authority or CA. This allows anyone with access to the certs to generate valid certs for any domain. Many of the man in the middle tools, such as Charles Proxy, make it very easy to download these CA certs. The hacker can then install these CA root certificates on a rooted phone and again proxy the traffic through a PC to see your unencrypted API calls.

Unencrypted HTTPS traffic

Figure 2: HTTPS Traffic

You can and should go one stage further and use SSL pinning. Your app will now only accept SSL certs that match the cert’s public key which you have hard coded into your app, see Figure 3. That way someone won’t be able to fake the SSL cert and perform a man in the middle attack. But unfortunately it’s not that hard to decompile or disassemble the Android app and comment out the code that checks for SSL pinning and your traffic is exposed again.

Pin It

Figure 3: SSL Pinning

The SafetyNet API is part of Google Play Services and has been around for a couple of years now and it looks like it could finally be a real solution to this SSL problem. SafetyNet allows you to determine with a reasonable degree of accuracy whether the Android phone is rooted or not. And if you know the phone is rooted then you can turn off any communication with the backend server to prevent man in the middle attacks on your HTTPS traffic.

SafetyNet works as follows, see Listing 1.

Listing 1

Listing 1: CTS request

  1. Create a single use token or nonce

  2. Send the compatibility check request

  3. Read the response and extract JSON Web Signature (or JWS)

  4. Validate the compatibility check response, i.e. true or false

  5. Decide to accept or reject the phone

Listing 2 shows a response from the SafetyNet API. The item we’re looking for is the ctsProfileMatch where CTS stands for the Compatibility Testing Suite. If that’s true then we can be reasonably happy that the phone has not been rooted.

{
  "nonce": "R2Rra24fVm5xa2Mg",
  "timestampMs": 9860437986543,
  "apkPackageName": "com.package.name.of.requesting.app",
  "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the certificate used to sign requesting app"],
  "apkDigestSha256": "base64 encoded, SHA-256 hash of the app's APK",
  "ctsProfileMatch": true,
  "basicIntegrity": true,
}

Listing 2: CTS response

There have been quite a few false positives so you might be rejecting some phones that you shouldn’t but when ctsProfileMatch is true then it’s a good indicator that the phone has or has not been rooted.

If you want to see if your phone passes the SafetyNet test then try the SafetyNet Helper App on Google Play. If you want to add the code to your app, look at the sample open source code from the same app SafetyNet code or try the Google Sample. Finally Learn2crack also have a good blog on the same topic.