【问题标题】:Shared Preferences Security共享偏好安全
【发布时间】:2016-07-14 08:00:21
【问题描述】:

我正在开发一个 android 模块,我必须使用共享首选项来存储 jwt 令牌以用于自动登录和其他一些事情。

我用密钥存储它,比如“令牌”或类似的东西。

问题是:

如果开发人员为他的应用程序导入我的模块,并找出密钥,他可以轻松读取我的 jwt 令牌,这对我不利。

您能提供一些替代解决方案吗?

编辑:我的最低 API 级别必须是 14。

【问题讨论】:

  • 我从来没有做过,但昨天碰巧搜索了它,看看这个github.com/sveinungkb/encrypted-userprefs
  • 存储首选项的目录受到保护。只要设备没有root,你就很好。
  • @MisterSmith,是的,我知道。但是使用我的模块的开发人员,如果他知道 jwt 的密钥,他可以很容易地从他的应用程序中读取它。

标签: android


【解决方案1】:

这个问题并不像看起来那么容易。据我所知,最好的解决方案是使用 NDK 以某种方式存储您的密钥;与使用简单的 Java 相比,C 代码更难反编译,并且您的保护级别更高。
Obfuscating Android Applications using O-LLVM and the NDK

另一种解决方案是使用字符串混淆器;但是,一般来说,通过默默无闻的安全从来都不是一个好主意。
Protect string constant against reverse-engineering

【讨论】:

  • 你的第一个想法似乎很有趣!
【解决方案2】:

您可以在保存到共享首选项之前加密您的令牌,当您需要使用时,您可以解密并使用它。

我建议您在保存到共享首选项时使用不可预测的密钥,而不是 "token"

这是一个 Encryption 类,可在 Android 应用中用于加密和解密数据。

public final class Encryption {
    private static final String CHIPHER_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final String GENERATE_KEY__ALGORITHM = "PBKDF2WithHmacSHA1";
    private static final String GENERATE_KEY_ALGORITHM = "AES";
    public static final int CRYPTO_TYPE_ENCRYPT = 0;
    public static final int CRYPTO_TYPE_DECRYPT = 1;

    public static String crypto(String inString, int type, String hashKey, String salt, String charset) {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(CHIPHER_TRANSFORMATION);
            byte[] inputByte = inString.getBytes(charset);
            switch (type) {
                case CRYPTO_TYPE_DECRYPT:
                    cipher.init(Cipher.DECRYPT_MODE, initKey(hashKey, salt));
                    return new String(cipher.doFinal(Base64.decode(inputByte, Base64.DEFAULT)));
                case CRYPTO_TYPE_ENCRYPT:
                    cipher.init(Cipher.ENCRYPT_MODE, initKey(hashKey, salt));
                    return new String(Base64.encode(cipher.doFinal(inputByte), Base64.DEFAULT));
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        }

        return null;
    }

    private static SecretKey getSecretKey(char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance(GENERATE_KEY__ALGORITHM);
        KeySpec spec = new PBEKeySpec(password, salt, 1024, 128);
        SecretKey tmp = factory.generateSecret(spec);
        return (new SecretKeySpec(tmp.getEncoded(), GENERATE_KEY_ALGORITHM));
    }

    private static SecretKey initKey(String hashKey, String salt) {
        try {
            return getSecretKey(hashKey.toCharArray(), salt.getBytes());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }
}

【讨论】:

  • 我之前说过,如果我使用AES,我必须将我的密码或密钥存储在某个地方,所以问题会被迭代。
  • Java 代码可以轻松反编译。因此,这只会阻止没有经验的攻击者的攻击。
  • 是的,你是对的,但我认为在 android 中没有关于共享首选项的 %100 安全方式。
【解决方案3】:

Android Keystore 系统可让您将私钥存储在容器中,从而更难从设备中提取私钥。一旦密钥在密钥库中,它们就可以用于加密操作,而私钥材料仍然不可导出。(注意:一个问题,它是在 API 级别 18 中引入的)

使用 Android Keystore 系统的 Android 安全共享首选项
https://github.com/ophio/secure-preferences

参考这篇文章,了解详细信息,
https://medium.com/@vashisthg/android-secure-shared-preferences-10f8356a4c2b#.8nf88g4g0

另一种解决方案 [API 级别 8]: Obscured Shared Preferences for Android

[ObscuredSharedPreferences.java] https://github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/com/worxforus/android/ObscuredSharedPreferences.java

希望对您有所帮助!

【讨论】:

  • 我编辑了我的问题,我必须使用 api 14。谢谢你的回答。
  • Android Keystore 适用于应用程序级别。当我们谈论第三方应用程序中的模块时,该应用程序可以访问该模块可以访问的所有键。因此,在这种情况下它不会有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 2019-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-21
相关资源
最近更新 更多