138

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

4

4 回答 4

131

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

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

  • 第三方应用程序不需要知道密码,用户可以确保他们只将密码发送到原始站点(Facebook、Twitter、Gmail 等)
  • 即使有人窃取了令牌,他们也看不到密码(用户也可能在其他网站上使用该密码)
  • 令牌通常有生命周期,并在一定时间后过期
  • 如果您怀疑令牌已被泄露,则可以撤销令牌
于 2012-04-15T16:03:52.883 回答
27

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

在此处输入图像描述

这是官方定义:

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

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

但是,最终 AccountManager 仅将您的令牌存储为纯文本。所以,我建议在将它们存储在 AccountManager 之前加密你的秘密。您可以使用各种加密库,如AESCryptAESCrypto

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

byte[] cipherText = crypto.encrypt(plainText);
byte[] plainText = crypto.decrypt(cipherText);
于 2017-03-14T17:09:51.860 回答
16

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

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

于 2018-03-01T09:13:25.473 回答
3
  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,当您从外部文件加载它们时,他们也永远不会获得您的访问令牌和秘密。

于 2018-01-04T06:18:30.430 回答