Securing your apps running from tampered Android devices.

Security is the biggest concern today and remains the most invested domain in IT industry. As a developer its always the foremost priority securing apps and data from unauthorized access.
What is root in Android ?
When you get the new phone it is protected by the google and has limited access. Though it allows to alter the behavior of the information access and device hardware api access, permission is needed beforehand. Rooting the phone gives you the root or sudo in more general admin access to the phone. Doing the root means your phone is like a linux machine and can do whatever you like. This is the great threat for the app maker which specially handles the card payment from the user.
How to protect app from running the in the rooted phone?
Running the app in root phone means the data you are transferring or taking from user remains highly vulnerable to be exposed by man in middle. So avoid this, google has introduced the service SafetyNet attestation API. This is the most efficient approach to find out whether your phone is rooted or not.
Steps
- Go the the google developer cloud console -> apis and services
- create the API credentials
- Search for ```Android device verification``` in search bar
- enable the service

Now you are ready to use in your application
public void safetyTest() {
if(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getApplicationContext(), 13000000) == ConnectionResult.SUCCESS) {
Log.e(TAG, "The SafetyNet Attestation API is available");
// TODO(developer): Change the nonce generation to include your own, used once value,
// ideally from your remote server.
String nonceData = "Safety Net Sample: " + System.currentTimeMillis();
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
Random mRandom = new SecureRandom();
byte[] bytes = new byte[24];
mRandom.nextBytes(bytes);
try {
byteStream.write(bytes);
byteStream.write(nonceData.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
byte[] nonce = byteStream.toByteArray();
SafetyNetClient client = SafetyNet.getClient(getApplicationContext());
Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, "YOUR_API_KEY");
task.addOnSuccessListener(attestationResponse -> {
/*
TODO(developer): Forward this result to your server together with
the nonce for verification.
You can also parse the JwsResult locally to confirm that the API
returned a response by checking for an 'error' field first and before
retrying the request with an exponential backoff.
NOTE: Do NOT rely on a local, client-side only check for security, you
must verify the response on a remote server!
*/
String jwsResult = attestationResponse.getJwsResult();
Log.e(TAG, "Success! SafetyNet result:\n" + jwsResult + "\n");
if (jwsResult == null) {
Log.e(TAG, "jwsResult Null");
}
final String[] jwtParts = jwsResult.split("\\.");
if (jwtParts.length == 3) {
String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT));
Log.e(TAG, "decodedPayload : " + decodedPayload);
presenter.safetyTest(decodedPayload);
}
});
task.addOnFailureListener( e -> {
// An error occurred while communicating with the service.
String mResult = null;
if (e instanceof ApiException) {
// An error with the Google Play Services API contains some additional details.
ApiException apiException = (ApiException) e;
Log.d(TAG, "Error: " +
CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
apiException.getStatusMessage());
showFailureDialog(CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
apiException.getStatusMessage());
} else {
// A different, unknown type of error occurred.
Log.e(TAG, "ERROR! " + e.getMessage());
showFailureDialog(e.getMessage());
}
});
} else {
Log.e(TAG, "Prompt user to update Google Play services.");
}
}
Copy above function and use it anywhere . If everything went well you will get the following response
{
"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",
}
if the device is not tampered ctsProfileMatch value will be true else will be false. Simple json decoding technique can be used to fetch the value from json and show the Dialogbox to prevent or notify user about the device compatibility.
More detailed information can be find in this link.
https://developer.android.com/training/safetynet/attestation