简体   繁体   English

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

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

I am going to use oAuth to fetch mails and contacts from google.我将使用 oAuth 从谷歌获取邮件和联系人。 I don't want to ask the user each time to log in to obtain an access token and secret.我不想每次都要求用户登录以获取访问令牌和秘密。 From what I understood, I need to store them with my application either in a database or SharedPreferences .据我了解,我需要将它们与我的应用程序一起存储在数据库或SharedPreferences中。 But I am a bit worried about security aspects with that.但我有点担心安全方面的问题。 I read that you can encrypt and decrypt the tokens but it is easy for an attacker to just decompile your apk and classes and get the encryption key.我读到你可以加密和解密令牌,但攻击者很容易反编译你的 apk 和类并获取加密密钥。
What's the best method to securely store these tokens in Android?将这些令牌安全地存储在 Android 中的最佳方法是什么?

Store them as shared preferences .将它们存储为共享首选项 Those are by default private, and other apps cannot access them.这些默认情况下是私有的,其他应用程序无法访问它们。 On a rooted devices, if the user explicitly allows access to some app that is trying to read them, the app might be able to use them, but you cannot protect against that.在有 root 权限的设备上,如果用户明确允许访问某个试图读取它们的应用程序,该应用程序可能能够使用它们,但您无法防止这种情况发生。 As for encryption, you have to either require the user to enter the decrypt passphrase every time (thus defeating the purpose of caching credentials), or save the key to a file, and you get the same problem.至于加密,您必须要求用户每次都输入解密密码(从而破坏了缓存凭据的目的),或者将密钥保存到文件中,您会遇到同样的问题。

There are a few benefits of storing tokens instead of the actual username password:存储令牌而不是实际的用户名密码有几个好处:

  • Third party apps don't need to know the password and the user can be sure that they only send it to the original site (Facebook, Twitter, Gmail, etc.)第三方应用不需要知道密码,用户可以确保他们只将密码发送到原始网站(Facebook、Twitter、Gmail 等)
  • Even if someone steals a token, they don't get to see the password (which the user might be using on other sites too)即使有人窃取了令牌,他们也看不到密码(用户也可能在其他网站上使用)
  • Tokens generally have a lifetime and expire after a certain time令牌通常具有生命周期并在一定时间后到期
  • Tokens can be revoked if you suspect they have been compromised如果您怀疑令牌已被盗用,则可以撤销令牌

You can store them in AccountManager .您可以将它们存储在AccountManager 中 It's considered best practice according to these guys.根据这些人的说法,这被认为是最佳实践。

在此处输入图片说明

Here's the official definition:这是官方定义:

This class provides access to a centralized registry of the user's online accounts.此类提供对用户在线帐户的集中注册表的访问。 The user enters credentials (username and password) once per account, granting applications access to online resources with "one-click" approval.用户为每个帐户输入一次凭据(用户名和密码),通过“一键式”批准授予应用程序访问在线资源的权限。

For detailed guide on how to use AccountManager:有关如何使用 AccountManager 的详细指南:

However, in the end AccountManager only stores your token as a plain text.但是,最终 AccountManager 仅将您的令牌存储为纯文本。 So, I would suggest encrypting your secret before storing them in AccountManager.因此,我建议在将您的机密存储在 AccountManager 之前对其进行加密。 You can utilize various Encryption library like AESCrypt or AESCrypto您可以使用各种加密库,如AESCryptAESCrypto

Another option is to use Conceal library .另一种选择是使用隐藏库 It's safe enough for Facebook and much easier to use than AccountManager.它对 Facebook 来说足够安全,而且比 AccountManager 更容易使用。 Here's a code snippet to save a secret file using Conceal.这是使用隐藏保存机密文件的代码片段。

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

SharedPreferences is not a secure location itself. SharedPreferences 本身并不是一个安全的位置。 On a rooted device we easily can read and modify all applications' SharedPrefereces xml's.在有 root 权限的设备上,我们可以轻松读取和修改所有应用程序的 SharedPrefereces xml。 So tokens should expire relatively frequent.所以令牌应该相对频繁地过期。 But even if a token expires every hour, newer tokens can still be stolen from SharedPreferences.但即使令牌每小时过期,仍然可以从 SharedPreferences 窃取较新的令牌。 Android KeyStore should be used for long term storage and retrieval of cryptographic keys which will be used to encrypt our tokens in order to store them in eg SharedPreferences or a database. Android KeyStore 应该用于长期存储和检索加密密钥,这些密钥将用于加密我们的令牌,以便将它们存储在例如 SharedPreferences 或数据库中。 The keys are not stored within an application's process, so they are harder to be compromised.密钥不存储在应用程序的进程中,因此它们更难被泄露。

So more relevant than a place is how they can be itself secure eg using cryptographically signed short-living JWTs, encrypting them using Android KeyStore and sending them with a secure protocol因此比地方更重要的是它们如何自身安全,例如使用加密签名的短期 JWT,使用 Android KeyStore 加密它们并使用安全协议发送它们

  1. From your Android Studio's Project pane, select "Project Files" and create a new file named "keystore.properties" in your project's root directory.从 Android Studio 的项目窗格中,选择“项目文件”并在项目的根目录中创建一个名为“keystore.properties”的新文件。

在此处输入图片说明

  1. Open "keystore.properties" file and save your Access Token and Secret in the file.打开“keystore.properties”文件并将您的访问令牌和秘密保存在该文件中。

在此处输入图片说明

  1. Now load the read the Access Token and Secret in your app module's build.gradle file.现在在您的应用程序模块的build.gradle文件中加载读取的访问令牌和秘密。 Then you need to define the BuildConfig variable for your Access Token and Secret so that you can you can directly access them from your code.然后您需要为您的访问令牌和秘密定义 BuildConfig 变量,以便您可以直接从您的代码访问它们。 Your build.gradle may look like following:您的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. You can use your Access Token and Secret in your code like this:您可以在代码中使用您的访问令牌和秘密,如下所示:

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

This way you don't need store the Access Token and Secret in plain text inside your project.这样你就不需要在你的项目中以纯文本形式存储访问令牌和秘密。 So even if someone decompiles your APK, they will never get your Access Token and Secret as you are loading them from a external file.因此,即使有人反编译了您的 APK,他们也永远不会在您从外部文件加载它们时获得您的访问令牌和机密。

Just as a late update to this question, you can now use EncryptedSharedPreferences to store data securely.正如这个问题的最新更新一样,您现在可以使用EncryptedSharedPreferences安全地存储数据。 The interface is very similar except that you also need to generate a MasterKey.界面非常相似,只是您还需要生成一个 MasterKey。

Most of the docs for EncryptedSharedPreferences use MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC, but that appears to be deprecated in favor of MasterKey.Builder . 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()

Well you can secure you access token by following two options. 您可以通过以下两个选项保护您访问令牌。

  1. Use save your access token into android keystore that would not be reverse. 使用将您的访问令牌保存到不会反向的android密钥库中。
  2. Use NDK function with some calculation that save your token and NDK with c++ code that is very hard to reverse 使用NDK函数进行一些计算,使用难以反转的c ++代码保存令牌和NDK

Auth0 provides a utility class to store tokens. Auth0 提供了一个实用程序 class 来存储令牌。 Its better to use that utility library.最好使用那个实用程序库。 There are two classes you can use to manage credentials:您可以使用两个类来管理凭据:

  1. CredentialsManager stores data in plain text CredentialsManager 以纯文本形式存储数据
  2. SecureCredentialsManager encrypts the data before storing it, using a combination of RSA and AES algorithms along with Android SecureCredentialsManager 在存储数据之前使用 RSA 和 AES 算法的组合以及 Android 对数据进行加密
    KeyStore.密钥库。

Link to documentation: Auth0.Android Save and Renew Tokens文档链接: Auth0.Android 保存和更新令牌

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

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