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

148

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


2
如何存储使用者密钥和密钥(硬编码不安全)?我需要它们来请求访问令牌和密钥。其他现有的OAuth应用是如何处理的呢?嗯,最后使用OAuth时,你需要更多地关注安全问题...我需要安全地保存消费者令牌/密钥以及访问令牌和密钥...最后,只存储用户的用户名/密码加密会不会更简单呢?...说到底,后者不是更好吗?我还是看不出OAuth的优势在哪里... - yeahman
你能告诉我哪个文件存储了访问令牌吗?我是 Android 的新手,尝试运行示例 Plus 应用程序。但是我无法找到 [GoogleAuthUtil.getToken() 方法]。 - Abhishek Kaushik
5个回答

137

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

与实际的用户名密码相比,存储令牌有以下几个好处:

  • 第三方应用程序不需要知道密码,用户可以确信它们只发送给原始站点(Facebook、Twitter、Gmail等)
  • 即使有人窃取令牌,他们也无法看到密码(用户可能还在其他网站上使用它们)
  • 令牌通常具有寿命,并在一定时间后过期
  • 如果怀疑已被攻击,可以吊销令牌

1
谢谢你的回复!但我怎么知道我的消费者密钥是否已经泄露了呢?哈哈,很难说...好的,关于存储访问令牌和密钥,我会将它们保存在sharedpreferences中并进行加密,但是消费者密钥和密钥怎么办?我不能将它们存储在sharedpreferences中(首先需要明确地编写代码来保存消费者密钥和密钥),不知道你是否明白我的意思。 - yeahman
2
你必须以某种(有点)混淆的方式将它们放在应用程序中,以便在反编译后不会立即显示出来,或者使用自己的授权代理 Web 应用程序,该应用程序具有密钥和密钥。将它们放在应用程序中显然更容易,如果您认为有人尝试破解您的应用程序的风险足够低,请采取这种方法。顺便说一句,上面的要点是针对用户密码的。如果您发现您的使用者密钥/密钥已被泄露,您也可以撤销这些密钥(当然,这将破坏您的应用程序)。 - Nikolay Elenkov
1
如果应用程序数据被清除,则刷新令牌将丢失,这可能不是用户想要的。 - rds
9
现在这种方式不再是最佳的令牌存储方式了! - Rahul Rastogi
3
@RahulRastogi什么是最好的方法? - elhefe
显示剩余4条评论

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

enter image description here

以下是官方定义:

该类提供对用户在线帐户的集中式注册表的访问。 用户仅一次输入凭据(用户名和密码),即可授权应用程序通过“单击”批准访问在线资源。

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

但是,最终 AccountManager 只会将您的令牌以纯文本形式存储。因此,我建议在将其存储在 AccountManager 之前对您的秘密进行加密。您可以利用各种加密库,例如 AESCryptAESCrypto

另一个选择是使用 Conceal library。它对 Facebook 来说足够安全,并且比 AccountManager 更容易使用。这是使用 Conceal 保存秘密文件的代码段。

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

2
很好的技巧,Conceal。看起来非常容易使用。适用于许多用例。 - lagos
无法通过链接找到Conceal。它可能已被停用。 - user1114

22

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

因此,比位置更相关的是它们本身如何保持安全,例如使用加密签名的短期JWT,在 Android KeyStore 中使用加密,并使用安全协议发送它们。


15
那么我们可以把它们存储在哪里? - Milind Mevada
2
通过使用Android账户管理器(手动加密,因为账户管理器只存储明文),或者Android密钥库来实现。 - Ezra Lazuardy
1
安全很难。每个人总是喋喋不休,最终每个解决方案都不是真正安全的。因为它就像一扇门;需要打开才能暴露内部内容,关闭以隐藏内部内容。诀窍在于如何知道打开门的人是否被允许和授权打开? - TheRealChx101

6

针对此问题,有一个最新的更新,你现在可以使用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
不要忘记在 build.gradle 中添加导入 implementation 'androidx.security:security-crypto:1.0.0'。 - htafoya

0

Auth0 提供了一个实用类来存储令牌。最好使用该实用库。有两个类可用于管理凭据:

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接