我将使用oAuth从Google提取邮件和联系人。我不想让用户每次登录都获得访问令牌和密码。据我了解,我需要将它们与应用程序一起存储在数据库或中SharedPreferences
。但是我对此有点担心。我读到您可以加密和解密令牌,但是攻击者很容易反编译apk和类并获取加密密钥。
在Android中安全存储这些令牌的最佳方法是什么?
我将使用oAuth从Google提取邮件和联系人。我不想让用户每次登录都获得访问令牌和密码。据我了解,我需要将它们与应用程序一起存储在数据库或中SharedPreferences
。但是我对此有点担心。我读到您可以加密和解密令牌,但是攻击者很容易反编译apk和类并获取加密密钥。
在Android中安全存储这些令牌的最佳方法是什么?
Answers:
将它们存储为共享首选项。默认情况下,这些是私有的,其他应用无法访问它们。在具有root用户权限的设备上,如果用户明确允许访问尝试读取它们的某些应用程序,则该应用程序可能能够使用它们,但是您不能对此加以防范。至于加密,您要么要求用户每次都输入解密密码(从而破坏了缓存凭据的目的),要么将密钥保存到文件中,您会遇到相同的问题。
存储令牌代替实际的用户名密码有一些好处:
您可以将它们存储在AccountManager中。这些家伙认为这是最佳做法。
这是官方定义:
此类提供对用户在线帐户的集中式注册表的访问。用户为每个帐户输入一次凭据(用户名和密码),以“一键式”批准向应用程序授予对在线资源的访问权限。
有关如何使用AccountManager的详细指南:
但是,最终AccountManager仅将您的令牌存储为纯文本。因此,我建议您先将您的机密加密,然后再将其存储在AccountManager中。您可以利用各种加密库,例如AESCrypt或 AESCrypto
另一种选择是使用隐蔽库。对于Facebook来说,它足够安全,并且比AccountManager更易于使用。这是使用Conceal保存机密文件的代码段。
byte[] cipherText = crypto.encrypt(plainText);
byte[] plainText = crypto.decrypt(cipherText);
SharedPreferences 本身不是一个安全的位置。在有根设备上,我们可以轻松读取和修改所有应用程序的SharedPrefereces xml。因此令牌应该相对频繁地过期。但是,即使令牌每小时过期一次,仍可以从SharedPreferences中窃取更新的令牌。Android KeyStore应该用于长期存储和检索加密密钥,这些密钥将用于加密我们的令牌,以便将其存储在例如SharedPreferences或数据库中。密钥没有存储在应用程序的进程中,因此很难被盗用。
因此,与地方相比,更重要的是如何使它们本身安全,例如,使用经过加密签名的短命JWT,使用Android KeyStore对其进行加密并使用安全协议进行发送
现在,将读取的访问令牌和密钥加载到应用程序模块的build.gradle文件中。然后,需要为访问令牌和密钥定义BuildConfig变量,以便可以直接从代码中访问它们。您的build.gradle可能如下所示:
... ... ...
android {
compileSdkVersion 26
// Load values from keystore.properties file
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
defaultConfig {
applicationId "com.yourdomain.appname"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// Create BuildConfig variables
buildConfigField "String", "ACCESS_TOKEN", keystoreProperties["ACCESS_TOKEN"]
buildConfigField "String", "SECRET", keystoreProperties["SECRET"]
}
}
您可以在代码中使用访问令牌和秘密,如下所示:
String accessToken = BuildConfig.ACCESS_TOKEN;
String secret = BuildConfig.SECRET;
这样,您就不需要在项目内以纯文本格式存储访问令牌和密钥。因此,即使有人反编译您的APK,当您从外部文件加载它们时,他们也永远不会获得您的访问令牌和秘密。