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




oauth preferences token (5)

SharedPreferences 不是安全的位置。 在有根的设备上,我们可以easily读取和修改所有应用程序的SharedPrefereces xml。 即使令牌每小时过期,新的令牌仍然可以从SharedPreferences中被盗取。 Android KeyStore应该用于长期存储和检索密钥,这些密钥将用于加密我们的令牌,以便将它们存储在例如SharedPreferences中。 密钥不存储在应用程序的进程中,所以它们很难被破坏。

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


您可以将它们存储在AccountManager 。 根据这些家伙,这被认为是最佳实践。

官方定义如下:

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

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

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

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

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

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

  1. 打开“keystore.properties”文件并将Access Token和Secret保存在文件中。

  1. 现在加载读取应用程序模块的build.gradle文件中的访问令牌和秘密。 然后,您需要为Access Token和Secret定义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,他们也永远不会得到你的访问令牌和秘密,因为你从外部文件加载它们。


那么你可以通过放弃两个选项来确保你获得令牌。

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

总结 :我会冒这个风险,并在客户端应用程序中保守秘密。

代理服务器选择

唯一可以合理缓解我列出的问题并进行代理工作的方法是整个九码 - 将处理第三方Web服务资源的所有业务逻辑移至代理服务器,并且使用丰富的用户界面使客户端应用程序哑终端。 这样,恶意应用程序能够代表其执行代理的唯一行为仅仅是您的业务逻辑合法需要的。

但是现在,你需要处理一系列其他问题,比如可靠性和可扩展性。

长时间考虑为什么简单代理不起作用

有些人遇到问题时,会想“我知道,我会添加自己的代理服务器”现在他们有两个问题。 (向Jamie Zawinski道歉)

你的假设基本上是正确的。 直到您开始考虑自己的服务器时,是否保留秘密并代理客户端应用程序的调用,或者尝试确定应用程序是否合法并将其提供给秘密。 在这两种方法中,您仍然必须解决“这个请求是否来自我写的一段代码”的问题?

让我重复一遍 - 没有办法区分特定软件运行的线路。 如果消息中的数据看起来不错,那么没有任何东西可以证明它是发送该消息的另一个应用程序

在一天结束的时候,如果我正在写一个恶意应用程序,我不在乎是否真的知道真正的秘密,只要我能让一个知道它代表我做一件事情的人知道。 因此,如果您认为恶意应用可以将您的应用模拟到第三方OAuth服务器,为什么您确定它无法将您的应用模拟到您的代理?

但是,等等,还有更多。 代理服务所在的域是您的客户和OAuth提供商的身份(由OAuth提供商向最终用户显示)。 如果一个恶意应用程序可以让你的服务器做坏事,不仅你的密钥被撤销,而且你的公共Web身份也不再被信任。

我将从明显的开始 - 无法区分特定软件运行的线路。 如果消息中的数据看起来不错,那么没有任何东西可以证明它是发送该消息的另一个应用程序。

因此,任何依赖于应用程序存储的秘密的算法都可能被欺骗。 OAuth的优势在于,它永远不会向应用程序提供用户的凭据,而是会为应用程序提供临时凭据,以便用户可以根据需要撤消该凭据。

当然,这里的弱点是,一个足够好的应用程序可以让用户信任它并且不会在完成其邪恶行为之前撤消证书。

然而,减少这种情况的一种方法是Google采用三段式OAuth而不是标准的两段式OAuth。 在三段式OAuth中,没有预先分配的秘密,但是在每次认证时,都会发出一个新的访问令牌密钥以及每个访问令牌。 尽管最终还是会有同样的缺点,因为糟糕的应用程序可能会从其进程中读取良好应用程序的令牌密钥,但它确实会导致用户每次需要新的访问令牌时都必须批准应用程序访问。

当然,这也意味着它对用户来说更加不方便和烦人。





android security oauth preferences token