问题
In my Andorid app, am using few keys and tokens for authentication and initialisations. I need to store these static keys somewhere in app securely. At the same time, I need to access it in code as well.
I am aware of SharedPreference and Gradle variables, which right now I use. I have tried Cryptography as well, but then I will have to store the secretKey also for decryption.
So, am searching for any workaround or proper solution. Any help will be highly appreciated.
回答1:
YOUR PROBLEM
Where to keep static information securely in Android app?
No matter where and how you store them, because from the moment you release your mobile app, any secret on it belongs now to the public domain.
I have tried Cryptography as well, but then I will have to store the secretKey also for decryption.
You can make this hard to be reverse engineered by static analysis by hiding it in the C code, using the JNI/NDK interface in Android, like I do in this Currency Converter Demo repo, but then if the attacker is not able to reverse engineer it this way, he will do it during run-time with an instrumentation framework, and a popular one is 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.
Another alternative it's to try to compute the secret keys at runtime, but then once more Frida will hook on the function that does this and extract the secret from it's return value.
A basic code example of computing an HMAC during run-time can be found the the ShipFast Demo repo:
private fun calculateAPIRequestHMAC(url: URL, authHeaderValue: String): String {
val secret = JniEnv().getHmacSecret()
var keySpec: SecretKeySpec
// Configure the request HMAC based on the demo stage
when (currentDemoStage) {
DemoStage.API_KEY_PROTECTION, DemoStage.APPROOV_APP_AUTH_PROTECTION -> {
throw IllegalStateException("calculateAPIRequestHMAC() not used in this demo stage")
}
DemoStage.HMAC_STATIC_SECRET_PROTECTION -> {
// Just use the static secret to initialise the key spec for this demo stage
keySpec = SecretKeySpec(Base64.decode(secret, Base64.DEFAULT), "HmacSHA256")
Log.i(TAG, "CALCULATE STATIC HMAC")
}
DemoStage.HMAC_DYNAMIC_SECRET_PROTECTION -> {
Log.i(TAG, "CALCULATE DYNAMIC HMAC")
// Obfuscate the static secret to produce a dynamic secret to initialise the key
// spec for this demo stage
val obfuscatedSecretData = Base64.decode(secret, Base64.DEFAULT)
val shipFastAPIKeyData = loadShipFastAPIKey().toByteArray(Charsets.UTF_8)
for (i in 0 until minOf(obfuscatedSecretData.size, shipFastAPIKeyData.size)) {
obfuscatedSecretData[i] = (obfuscatedSecretData[i].toInt() xor shipFastAPIKeyData[i].toInt()).toByte()
}
val obfuscatedSecret = Base64.encode(obfuscatedSecretData, Base64.DEFAULT)
keySpec = SecretKeySpec(Base64.decode(obfuscatedSecret, Base64.DEFAULT), "HmacSHA256")
}
}
Log.i(TAG, "protocol: ${url.protocol}")
Log.i(TAG, "host: ${url.host}")
Log.i(TAG, "path: ${url.path}")
Log.i(TAG, "Authentication: $authHeaderValue")
// Compute the request HMAC using the HMAC SHA-256 algorithm
val hmac = Mac.getInstance("HmacSHA256")
hmac.init(keySpec)
hmac.update(url.protocol.toByteArray(Charsets.UTF_8))
hmac.update(url.host.toByteArray(Charsets.UTF_8))
hmac.update(url.path.toByteArray(Charsets.UTF_8))
hmac.update(authHeaderValue.toByteArray(Charsets.UTF_8))
return hmac.doFinal().toHex()
}
Bear in mind this is a simple solution, but even a sophisticated one would be vulnerable to Frida scripts used by an attacker.
SECURITY IN DEPTH
So, am searching for any workaround or proper solution. Any help will be highly appreciated.
Security is all about adding as many layers as you can afford in order to make it time consuming for an attacker to overcome all of them, and to raise the bar for the skill set necessary by an attacker.
So using C code to hide secrets, like the decryption keys, store encrypted secrets on the Android keystore will throw away the scripts kids, but will leave you vulnerable to attackers that know how to use Frida scripts to hook into your code.
In my Andorid app, am using few keys and tokens for authentication and initialisations.
If your are trying to secure the keys to access your API then you can read my answer to this question to understand that implementing the Mobile App Attestation concept will allow you to not need to store secrets to access your API server. For initializations purposes I would recommend that you move this logic to the backend, because any in app decisions can be modified/bypassed with instrumentation Frameworks
Also consider using strong obfuscation techniques for all your code base, that will add another layer of difficulty in the attacker steps to reverse engineer your mobile app.
DO YOU WANT TO GO THE EXTRA MILE?
In any response to a security question I always like to reference the excellent work from the OWASP foundation.
For Mobile Apps
OWASP Mobile Security Project - Top 10 risks
The OWASP Mobile Security Project is a centralized resource intended to give developers and security teams the resources they need to build and maintain secure mobile applications. Through the project, our goal is to classify mobile security risks and provide developmental controls to reduce their impact or likelihood of exploitation.
OWASP - Mobile Security Testing Guide:
The Mobile Security Testing Guide (MSTG) is a comprehensive manual for mobile app security development, testing and reverse engineering.
For APIS
OWASP API Security Top 10
The OWASP API Security Project seeks to provide value to software developers and security assessors by underscoring the potential risks in insecure APIs, and illustrating how these risks may be mitigated. In order to facilitate this goal, the OWASP API Security Project will create and maintain a Top 10 API Security Risks document, as well as a documentation portal for best practices when creating or assessing APIs.
来源:https://stackoverflow.com/questions/61724202/where-to-keep-static-information-securely-in-android-app