Ahmad Sattout
Ahmad Sattout

Reputation: 2476

Securing API key in Android

Scenario :

Say you have an Android Application that uses some API on the server.

The application doesn't have a login funcionality (there is no need for it as the user doesn't have any personal data on the server)

In order to send a request to the server, you create an OAuth2 functionality on your server. You make a request to the server and it returns an Auth Token.

But this request can be easily taken if a user monitors network requests. So you create a secret key to be sent with the POST body of the Auth request.

The problem starts here :

This Secret Key that you created has to stay in the application, and any code in the application can be broken down (decompiled) and the secret would be accessable.

The key can't be on the server, or it will take a request to acquire it, and thus it can be monitored.

You can't encode it in any way, as the decoding functionality will have to be on the Android side, and thus can be decompiled.

The question :

Is there any way that is possible to at least make the Secret Key much harder to decode?

P.S : Using R8 or Proguard only obscures the code. If the hacker or attacker tries, they can easily see the entire code of the application, and can follow along to understand the flow of encoding or decoding, or find the key inside the code.

Upvotes: 3

Views: 2258

Answers (2)

Anton Baranenko
Anton Baranenko

Reputation: 644

Assuming you need to keep a secret key on the app side, you can harden the application against key lifting, reverse engineering, and tampering.

If you are already familiar with Proguard/R8, then you may find DexGuard to be a good fit for your use case.

Alternatively, you can also think of changing your application logic so that storing of a secret key on the app side is not required.

In your decision making you will need to balance:

  • Security vs. Usability. For example, you may decide to require registration and authentication from your users instead of working without authentication.
  • Risk vs. Cost. Depending on the risk associated with your API key exposure, you may decide to invest in a commercial solution or use free/DIY solutions.

Upvotes: 0

Exadra37
Exadra37

Reputation: 13054

SCENARIO

In order to send a request to the server, you create an OAuth2 functionality on your server. You make a request to the server and it returns an Auth Token.

Here you are exposing yourself to Trust on First Use(TOFU) of the application, meaning that the API server would have to accept that the request comes indeed from what you trust to be your genuine and untampered mobile app.

But this request can be easily taken if a user monitors network requests. So you create a secret key to be sent with the POST body of the Auth request.

This is as easy as doing a Man in the Middle(MitM) attack with a tool like mitmproxy or wireshark, and it's all it takes for the attacker to learn how to do the request as if he was what you think as being your genuine mobile app, therefore without the need to stole the secret key in the POST body of the Auth request he MitM attacking.

TOFU:

Trust on first use (TOFU), or trust upon first use (TUFU), is a security model used by client software which needs to establish a trust relationship with an unknown or not-yet-trusted endpoint.

MitM:

An interactive TLS-capable intercepting HTTP proxy for penetration testers and software developers.

Wireshark

Wireshark is a network traffic analyzer, or "sniffer", for Linux, macOS, *BSD and other Unix and Unix-like operating systems and for Windows.

THE PROBLEM STARTS HERE

This Secret Key that you created has to stay in the application, and any code in the application can be broken down (decompiled) and the secret would be accessable.

Yes this is indeed a huge issue, and even when you hide the secret in native C code, like I show in this repo, you can still use tools for static analysis on the binary of your mobile app, like the MobSF, to extract any secrets on it or to find logic that deals with them.

You can't encode it in any way, as the decoding functionality will have to be on the Android side, and thus can be decompiled.

Once more you are correct, and all it takes is to use the already mentioned MobSF, or go more advanced by hooking an instrumentation framework to the code at runtime, that will allow to extract dynamically the secret key, and/or to modify any behavior of your code. A good example of such tool is Frida

MobSF:

Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis.

Frida:

Inject your own scripts into black box processes. Hook any function, spy on crypto APIs or trace private application code, no source code needed. Edit, hit save, and instantly see the results. All without compilation steps or program restarts.

THE QUESTION

Is there any way that is possible to at least make the Secret Key much harder to decode?

As I already mention, Frida can be used to modify the behavior of your code during the runtime, thus the attacker would find the code doing this and hook on it's return to extract the already decoded secret key.

Possible Solution

Better than hardening the secret is to not have a secret at all in the mobile app code, be it static or dynamically generated by the mobile app itself at runtime. To achieve this you will need to look into the Mobile App Attestation concept.

The Role of Mobile App Attestation

This will be a solution that will allow your API server to know that what is making the request is indeed the genuine mobile app you have uploaded to the Google Play store, not an automated script, a Postman request, or any other form of impersonation. It will consist of an attestation layer running on the cloud that will challenge the mobile app, and based on the measures returned it will issue short lived JWT tokens for the mobile app to pass down in each request to the API server, that will verify them for a valid signature and expire time. I go in more detail on this section of my article:

Before we dive into the role of a Mobile App Attestation service, we first need to understand the difference between what and who is accessing the API server. This is discussed in more detail in this article, where we can read:

The what is the thing making the request to the API server. Is it really a genuine instance of your mobile app, or is it a bot, an automated script or an attacker manually poking around your API server with a tool like Postman?

The who is the user of the mobile app that we can authenticate, authorize and identify in several ways, like using OpenID Connect or OAUTH2 flows.

The role of a Mobile App Attestation service is to authenticate what is sending the requests, thus only responding to requests coming from genuine mobile app instances and rejecting all other requests from unauthorized sources.

In order to know what is sending the requests to the API server, a Mobile App Attestation service, at run-time, will identify with high confidence that your mobile app is present, has not been tampered/repackaged, is not running in a rooted device, has not been hooked into by an instrumentation framework(Frida, xPosed, Cydia, etc.), and is not the object of a Man in the Middle Attack (MitM). This is achieved by running an SDK in the background that will communicate with a service running in the cloud to attest the integrity of the mobile app and device it is running on.

On a successful attestation of the mobile app integrity, a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud know. In the case that attestation fails the JWT token is signed with an incorrect secret. Since the secret used by the Mobile App Attestation service is not known by the mobile app, it is not possible to reverse engineer it at run-time even when the app has been tampered with, is running in a rooted device or communicating over a connection that is the target of a MitM attack.

The mobile app must send the JWT token in the header of every API request. This allows the API server to only serve requests when it can verify that the JWT token was signed with the shared secret and that it has not expired. All other requests will be refused. In other words a valid JWT token tells the API server that what is making the request is the genuine mobile app uploaded to the Google or Apple store, while an invalid or missing JWT token means that what is making the request is not authorized to do so, because it may be a bot, a repackaged app or an attacker making a MitM attack.

A great benefit of using a Mobile App Attestation service is its proactive and positive authentication model, which does not create false positives, and thus does not block legitimate users while it keeps the bad guys at bay.

SUMMARY

Defending API servers and mobile apps from being attacked is a very hard job has you already know, judging by the content of your question, and that I tried to make more explicit for anyone reading this answer, by providing more context and tools used to do it so.

In the end of the day is all about security in depth, and you should apply as many layers of defense as you can afford, are required by law to your use case, and that you judge adequate for the value of the data your are trying to protect.

GOING FOR THE EXTRA MILE

In any security questions I reply, I always like to finish by recommending the excellent work from OWASP, specially this one:

The Mobile Security Testing Guide

The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security development, testing and reverse engineering.

Upvotes: 7

Related Questions