繁体   English   中英

如何在 Android 中安全地存储访问令牌和秘密?

[英]How to securely store access token and secret in Android?

我将使用 oAuth 从谷歌获取邮件和联系人。 我不想每次都要求用户登录以获取访问令牌和秘密。 据我了解,我需要将它们与我的应用程序一起存储在数据库或SharedPreferences中。 但我有点担心安全方面的问题。 我读到你可以加密和解密令牌,但攻击者很容易反编译你的 apk 和类并获取加密密钥。
将这些令牌安全地存储在 Android 中的最佳方法是什么?

将它们存储为共享首选项 这些默认情况下是私有的,其他应用程序无法访问它们。 在有 root 权限的设备上,如果用户明确允许访问某个试图读取它们的应用程序,该应用程序可能能够使用它们,但您无法防止这种情况发生。 至于加密,您必须要求用户每次都输入解密密码(从而破坏了缓存凭据的目的),或者将密钥保存到文件中,您会遇到同样的问题。

存储令牌而不是实际的用户名密码有几个好处:

  • 第三方应用不需要知道密码,用户可以确保他们只将密码发送到原始网站(Facebook、Twitter、Gmail 等)
  • 即使有人窃取了令牌,他们也看不到密码(用户也可能在其他网站上使用)
  • 令牌通常具有生命周期并在一定时间后到期
  • 如果您怀疑令牌已被盗用,则可以撤销令牌

您可以将它们存储在AccountManager 中 根据这些人的说法,这被认为是最佳实践。

在此处输入图片说明

这是官方定义:

此类提供对用户在线帐户的集中注册表的访问。 用户为每个帐户输入一次凭据(用户名和密码),通过“一键式”批准授予应用程序访问在线资源的权限。

有关如何使用 AccountManager 的详细指南:

但是,最终 AccountManager 仅将您的令牌存储为纯文本。 因此,我建议在将您的机密存储在 AccountManager 之前对其进行加密。 您可以使用各种加密库,如AESCryptAESCrypto

另一种选择是使用隐藏库 它对 Facebook 来说足够安全,而且比 AccountManager 更容易使用。 这是使用隐藏保存机密文件的代码片段。

byte[] cipherText = crypto.encrypt(plainText);
byte[] plainText = crypto.decrypt(cipherText);

SharedPreferences 本身并不是一个安全的位置。 在有 root 权限的设备上,我们可以轻松读取和修改所有应用程序的 SharedPrefereces xml。 所以令牌应该相对频繁地过期。 但即使令牌每小时过期,仍然可以从 SharedPreferences 窃取较新的令牌。 Android KeyStore 应该用于长期存储和检索加密密钥,这些密钥将用于加密我们的令牌,以便将它们存储在例如 SharedPreferences 或数据库中。 密钥不存储在应用程序的进程中,因此它们更难被泄露。

因此比地方更重要的是它们如何自身安全,例如使用加密签名的短期 JWT,使用 Android KeyStore 加密它们并使用安全协议发送它们

  1. 从 Android Studio 的项目窗格中,选择“项目文件”并在项目的根目录中创建一个名为“keystore.properties”的新文件。

在此处输入图片说明

  1. 打开“keystore.properties”文件并将您的访问令牌和秘密保存在该文件中。

在此处输入图片说明

  1. 现在在您的应用程序模块的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"] } }
  2. 您可以在代码中使用您的访问令牌和秘密,如下所示:

     String accessToken = BuildConfig.ACCESS_TOKEN; String secret = BuildConfig.SECRET;

这样你就不需要在你的项目中以纯文本形式存储访问令牌和秘密。 因此,即使有人反编译了您的 APK,他们也永远不会在您从外部文件加载它们时获得您的访问令牌和机密。

正如这个问题的最新更新一样,您现在可以使用EncryptedSharedPreferences安全地存储数据。 界面非常相似,只是您还需要生成一个 MasterKey。

EncryptedSharedPreferences 的大多数文档都使用 MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC,但似乎不推荐使用MasterKey.Builder

private var masterKeyAlias = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build() 

private val preferences = EncryptedSharedPreferences.create(
            context,
            "auth_token_secured",
            masterKeyAlias,
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        )

var authToken: String?
    get() = preferences.getString("auth_token", "")
    set(value) = preferences.edit().putString("auth_token", value).apply()

您可以通过以下两个选项保护您访问令牌。

  1. 使用将您的访问令牌保存到不会反向的android密钥库中。
  2. 使用NDK函数进行一些计算,使用难以反转的c ++代码保存令牌和NDK

Auth0 提供了一个实用程序 class 来存储令牌。 最好使用那个实用程序库。 您可以使用两个类来管理凭据:

  1. CredentialsManager 以纯文本形式存储数据
  2. SecureCredentialsManager 在存储数据之前使用 RSA 和 AES 算法的组合以及 Android 对数据进行加密
    密钥库。

文档链接: Auth0.Android 保存和更新令牌

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM