问题描述
在我的Andorid应用中,正在使用很少的密钥和令牌进行身份验证和初始化.我需要将这些静态密钥安全地存储在应用程序中的某个位置.同时,我还需要通过代码访问它.我知道现在使用的SharedPreference和Gradle变量.我也尝试过加密技术,但是随后我还必须存储secretKey以便解密.
因此,我正在寻找任何解决方法或适当的解决方案.任何帮助将不胜感激.
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.
推荐答案
您的问题
无论在何处以及如何存储它们,因为从发布移动应用程序之时起,该应用程序上的任何秘密现在都属于公共领域.
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.
您可以通过使用Android中的JNI/NDK接口将其隐藏在C代码中,从而使静态分析难以逆向工程,就像我在货币转换器演示回购,但是如果攻击者是不能以这种方式进行逆向工程,他将在运行时使用一个检测框架来进行此操作,并且流行的是弗里达(Frida):
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:
另一种替代方法是尝试在运行时计算秘密密钥,但随后Frida将再次钩住执行此操作的函数并从其返回值中提取秘密.
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.
可以在 ShipFast演示回购:
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()
}
请记住,这是一个简单的解决方案,但是即使是复杂的解决方案也容易受到攻击者使用的Frida脚本的攻击.
深度安全
Bear in mind this is a simple solution, but even a sophisticated one would be vulnerable to Frida scripts used by an attacker.
安全就是增加尽可能多的层次,以使攻击者花费大量时间来克服所有这些层次,并提高攻击者必需的技能门槛.
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.
因此,使用C代码隐藏诸如解密密钥之类的机密,将加密的机密存储在Android密钥库中会丢掉孩子的脚本,但使您容易受到攻击者的攻击,他们知道如何使用Frida脚本来钩住您的代码.
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.
如果您尝试保护访问API的密钥,则可以阅读对,以了解实施移动应用证明概念将使您无需存储密码即可访问您的API服务器.出于初始化目的,我建议您将此逻辑移至后端,因为任何应用内决策都可以使用检测框架进行修改/绕过
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.
在回答安全问题时,我总是喜欢引用OWASP基金会的出色工作.
In any response to a security question I always like to reference the excellent work from the OWASP foundation.
对于APIS
这篇关于在Android应用程序中哪里可以安全地保存静态信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!