我将使用 oAuth 从 google 获取邮件和联系人。我不想每次都要求用户登录以获取访问令牌和秘密。据我了解,我需要将它们与我的应用程序一起存储在数据库或SharedPreferences
. 但我有点担心安全方面的问题。我读到您可以加密和解密令牌,但攻击者很容易只需反编译您的 apk 和类并获取加密密钥。
在 Android 中安全存储这些令牌的最佳方法是什么?
4 回答
将它们存储为共享首选项。这些默认情况下是私有的,其他应用程序无法访问它们。在有根设备上,如果用户明确允许访问某些试图读取它们的应用程序,则该应用程序可能能够使用它们,但您无法防止这种情况发生。至于加密,您必须要求用户每次都输入解密密码(从而破坏了缓存凭据的目的),或者将密钥保存到文件中,您会遇到同样的问题。
存储令牌而不是实际的用户名密码有一些好处:
- 第三方应用程序不需要知道密码,用户可以确保他们只将密码发送到原始站点(Facebook、Twitter、Gmail 等)
- 即使有人窃取了令牌,他们也看不到密码(用户也可能在其他网站上使用该密码)
- 令牌通常有生命周期,并在一定时间后过期
- 如果您怀疑令牌已被泄露,则可以撤销令牌
您可以将它们存储在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 加密它们并使用安全协议发送它们
- 从 Android Studio 的项目窗格中,选择“项目文件”并在项目的根目录中创建一个名为“keystore.properties”的新文件。
- 打开“keystore.properties”文件并将您的访问令牌和秘密保存在文件中。
现在在您的应用程序模块的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,当您从外部文件加载它们时,他们也永远不会获得您的访问令牌和秘密。