我应该将令牌安全地保存在内存还是 sqlite 中?或请建议

Posted

技术标签:

【中文标题】我应该将令牌安全地保存在内存还是 sqlite 中?或请建议【英文标题】:Should I Save token securely in Memory or sqlite? or Please suggest 【发布时间】:2017-08-12 20:55:05 【问题描述】:

几天前我开始学习 android,到目前为止,我已经完成了 Login Activity、Main Activity 的实现,它扩展了抽象的 Base Activity。

点击导航栏项目时,会从 Fragments 中打开 xml。

我对成功登录后收到的令牌有疑问。此令牌与每个请求一起使用,以在成功登录后获取数据。我应该将令牌安全地保存在 sqlite 数据库中,还是应该在 Main Activity 中创建一个公共属性? Main Activity 将始终保留在内存中,因为这会打开片段。

【问题讨论】:

你可以使用sqlite database 这完全取决于您的应用程序设计。应用重启时会要求登录吗?你会保存会话吗?您希望它有多安全?如果手机已root,则可以提取和读取SQLite。 在实施以下提供的解决方案时要格外小心。我阅读的大部分内容都不安全。祝你好运。 想想如果令牌泄露会发生什么。它可以在其他设备上使用吗?它是否永远有效?用户可以注销并使其无效吗?攻击者可以获得的信息是否敏感?这完全取决于您希望您的应用程序有多安全。 在 sqlite 中存储不是一个好主意。如果未在清单中正确设置,则可以导出数据库。如果设备已植根,则更容易。 【参考方案1】:

我可以建议 3 个选项: 1)您可以将令牌保存到文件中,如下所示:

public static void saveToken(Context ctx, String fileName, Object token) 
        if (token == null) 
            ctx.deleteFile(fileName);
         else 
            ObjectOutputStream out = null;
            try 
                FileOutputStream fout = ctx.openFileOutput(fileName, 0);
                out = new ObjectOutputStream(fout);
                out.writeObject(token);
                fout.getFD().sync();
             catch (Exception e) 
                e.printStackTrace();
             finally 
                try 
                    if (out != null)
                        out.close();
                 catch (Exception e) 
                    e.printStackTrace();
                
            
        
    

确保对象 token 实现 java.io.Serializable interface

API 级别 24 之前的用法:

saveToken(appContext, someFileName, someTokenObject);

使用 API 级别 24 及更高级别:

saveToken(appContext.createDeviceProtectedStorageContext(), someFileName, someTokenObject);

2) 使用 SQLCipher 库加密数据库。

3) 您可以使用密钥库系统https://developer.android.com/training/articles/keystore.html 加密您的令牌

【讨论】:

【参考方案2】:

使用SharedPreferences 并确保您使用的是Context.MODE_PRIVATE,这样只有您的应用才能访问数据。 SharedPreferences 是一个持久化存储

例如

SharedPreferences prefs = context.getPreferences(Context.MODE_PRIVATE);
prefs.edit().putString("token", token).apply();
token = prefs.getString("token");

为什么不使用 SQLite:

SQLite 是一个数据库,针对表格数据,单个标记不适合这种用例。

为什么不存储在主Activity中:

主要活动在应用程序安装的整个生命周期内都不会存在,它可以随时被操作系统清理。它不是持久数据存储。

【讨论】:

【参考方案3】:

我应该将令牌安全地保存在 sqlite 数据库中,还是应该在 Main Activity 中创建一个公共属性? Main Activity 将始终保留在内存中,因为这会打开片段。

Official Android documentation 已经在“安全和隐私的最佳实践”部分回答了您的问题。它给出了以下声明:

如果您有权访问用户数据并且可以避免存储或传输它,请不要存储或传输数据

换句话说,如果你可以避免持久化,那么不要持久化它

您在问题中提到了“公共财产”,这让我想知道可见性修饰符的概念是否还不清楚。 Java publicprivate 修饰符用于 controlling access to the members of your class。根据this answer here,它们与安全无关。

如果您确实将令牌保存在内存中,作为public 字段或其他方式,您可以通过将令牌存储在char[] 而不是String 中来稍微减少您的曝光。 this canonical answer 中也对此进行了详细说明。

最后,如果您必须存储令牌,sqlite 数据库不是存储令牌的正确位置。相反,您应该使用提供的KeyStore,这将使在设备受到威胁的情况下更难以提取令牌。上述文档的链接包含代码示例。如果这被证明太难使用,它周围有一些包装器,包括Scytale。

【讨论】:

【参考方案4】:

1) 将令牌值存储在基本应用程序单例中(您的应用程序必须是 BaseApplication 的实例)

public class BaseApplication extends Application 
    // token
    private String token = null;
    public String getToken() return this.token;
    public void setToken(String token) this.token = token;

通过上面的实现,您将能够从任何活动/片段中设置和获取令牌值。但是该值不是持久的,一旦应用程序结束就会丢失。

备注:如果您使用令牌进行 REST api 访问,那么您可以使用与上述类似的解决方案将令牌存储在后台服务实例中。

2) 使用 SharedPreferences - 如果您想在应用程序运行之间存储令牌的值,这是推荐的方式。

请看@Ryan 的回答。

【讨论】:

【参考方案5】:

您可以使用 SharedPreferences 来存储令牌。它可以通过应用程序使用。

【讨论】:

【参考方案6】:

您可以将其存储在 Shared Preference 中,因为这是令牌。

现在来到安全部分,您​​显然可以对共享偏好使用加密。

例如,您可以在下面的库中使用很多未完成的项目 https://github.com/ophio/secure-preferences 关于您的 java 文件中要加密的密钥,您需要确保在将其上传到 playstore 之前应用了 proguard。

以这种方式,现在您的令牌通过共享偏好完全安全。

为了将其保存在 sqlite 中,而不是通过解码或 root 访问,您的 db 文件也可以像首选项一样访问。关于设置中的清除数据,我认为它也会删除您的 sqlite 数据。不过不确定。

希望对你有所帮助。

【讨论】:

You need to be sure you are applying proguard before you upload it to playstore 隐藏代码并不意味着它是安全的。更不用说 ProGuard 不会混淆字符串 IIRC。 你能从 Proguard 应用的代码中获取特定字符串的价值吗?你能找到它属于哪个班级吗? ***.com/questions/10220069/… @JonZarate ,可以找到,但这是更好的主意。 @MushahidKhatri It will be hard to find key if it is not static and proguard is applied 假。你只是说你一直不知道的事情。 You just gone silent 我知道解决方案为什么对你很重要?不会改变您的方法易受攻击的事实。反正,是的,我解决了,但跟你解释也没用,因为你不注意我说的话。【参考方案7】:

最好使用SqliteRealm。并存储在应用程序内存中而不是外部内存中。至于驻留在应用程序内存中的数据,我们无需过多担心安全性。保存在 MainActivity 不是一个好的解决方案,因为一旦应用程序关闭,它就会被清除。

存储在Shared Preference 也是一种选择。但是如果用户从设置中清除缓存,这个值也会被清除。 Realm Android Reference Link

【讨论】:

也许你应该评论每个的安全方面。 AFAIK,这两个建议都很脆弱。 @JonZarate 那么您心中的解决方案是什么?我可以知道,没有漏洞? 有一些第三方库可以保证数据库的安全。

以上是关于我应该将令牌安全地保存在内存还是 sqlite 中?或请建议的主要内容,如果未能解决你的问题,请参考以下文章

将 ImageView 准确地保存在 XML 位置

在哪里存储 JWT 令牌?

我应该将 sqlite 数据库文件写入 Documents 目录还是 Library/Caches?

每日安全资讯SQLite 被曝存在漏洞 所有 Chromium 浏览器受影响

JSON Web 令牌 (JWT):我应该使用响应标头还是正文进行令牌传输?

sqlite还是文件存储?