Android 在应用程序中存储和保护私有API密钥的最佳实践

Android 在应用程序中存储和保护私有API密钥的最佳实践,android,reverse-engineering,proguard,api-key,Android,Reverse Engineering,Proguard,Api Key,大多数应用程序开发人员会将一些第三方库集成到他们的应用程序中。如果是访问服务,如Dropbox或YouTube,或是记录崩溃。第三方图书馆和服务的数量惊人。大多数库和服务都是通过某种方式与服务进行身份验证来集成的,大多数情况下,这是通过API密钥实现的。出于安全目的,服务通常生成公共和私有密钥,通常也称为机密密钥。不幸的是,为了连接到服务,必须使用该私钥进行身份验证,因此可能是应用程序的一部分。 不用说,这面临着巨大的安全问题。公共和私有API密钥可以在几分钟内从APK中提取,并且可以很容易地实

大多数应用程序开发人员会将一些第三方库集成到他们的应用程序中。如果是访问服务,如Dropbox或YouTube,或是记录崩溃。第三方图书馆和服务的数量惊人。大多数库和服务都是通过某种方式与服务进行身份验证来集成的,大多数情况下,这是通过API密钥实现的。出于安全目的,服务通常生成公共和私有密钥,通常也称为机密密钥。不幸的是,为了连接到服务,必须使用该私钥进行身份验证,因此可能是应用程序的一部分。 不用说,这面临着巨大的安全问题。公共和私有API密钥可以在几分钟内从APK中提取,并且可以很容易地实现自动化

假设我有类似的东西,我如何保护密钥:

public class DropboxService  {

    private final static String APP_KEY = "jk433g34hg3";
    private final static String APP_SECRET = "987dwdqwdqw90";
    private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;

    // SOME MORE CODE HERE

}
您认为存储私钥的最佳和最安全的方法是什么?模糊处理,加密,你怎么看

应用程序密钥应保持私有-但在发布应用程序时 他们可以被一些人逆转

对于那些不会隐藏代码的人,请锁定
ProGuard
代码。这是一个重构,一些付费的模糊处理程序正在插入一些位运算符以获取
jk433g34hg3
一串如果你工作3天,你可以使黑客攻击延长5-15分钟:)

最好的办法就是保持现状,伊姆霍

即使您将密钥存储在服务器端(您的PC),密钥也可能被破解并打印出来。也许这需要最长的时间?无论如何,最好的情况是几分钟或几小时


普通用户不会对您的代码进行反编译。

我认为只有第一种方法可以提供一些保证:

  • 把你们的秘密保存在互联网上的某个服务器上,必要时只要抓住它们并使用它就行了。若用户将要使用dropbox,那个么并没有什么可以阻止你们向你们的站点提出请求并获取你们的密钥

  • 将您的秘密放在jni代码中,添加一些可变代码,使您的库更大,更难反编译。您还可以将键字符串拆分为几个部分,并将它们保留在不同的位置

  • 使用模糊处理程序,也可以将代码放入哈希机密中,然后在需要使用时将其取消哈希

  • 将您的密钥作为资源中某个图像的最后一个像素。然后在需要的时候在代码中阅读它。混淆代码应该有助于隐藏将读取它的代码

  • 如果您想快速了解读取apk代码有多容易,请抓取APKAnalyser:

  • 实际上,编译后的应用程序包含密钥字符串,但也包含常量名称APP_key和APP_SECRET。从这样的自文档代码中提取密钥非常简单,例如使用标准的Android工具dx

  • 你可以申请ProGuard。它将保留键字符串不变,但将删除常量名称。它还将尽可能使用简短、无意义的名称重命名类和方法。然后提取键需要更多的时间,以确定哪个字符串用于哪个目的

    请注意,设置ProGuard不应该像您担心的那样困难。首先,您只需要启用ProGuard,如project.properties中所述。如果第三方库存在任何问题,您可能需要在proguard-project.txt中抑制某些警告和/或防止它们被混淆。例如:

    这是一种暴力手段;一旦处理后的应用程序工作,您就可以优化此类配置

  • 您可以在代码中手动混淆字符串,例如使用Base64编码或更复杂的编码;甚至可能是本地代码。然后,黑客将不得不对您的编码进行静态反向工程,或在适当的位置动态拦截解码

  • 您可以应用商业模糊器,如ProGuard的专用同级模糊器。它还可以为您加密/模糊字符串和类。提取密钥需要更多的时间和专业知识

  • 您可以在自己的服务器上运行部分应用程序。如果你能把钥匙放在那里,它们是安全的

  • 最后,这是一个你必须做出的经济权衡:钥匙有多重要,你能负担得起多少时间或软件,对钥匙感兴趣的黑客有多老练,他们想花多少时间,钥匙被破解前的延迟值多少,成功的黑客会以多大的规模分发密钥等。像密钥这样的小块信息比整个应用程序更难保护。从本质上讲,客户端没有什么是牢不可破的,但您肯定可以提高标准


    (我是ProGuard和DexGuard的开发者)

    这篇文章已经过时了,但仍然足够好。我想把它藏在一个.so库里,当然可以,使用NDK和C++。因此,文件可以在十六进制编辑器中查看,但祝你好运,反编译:P

    保持这些私有的唯一真正方法是将它们保留在服务器上,让应用程序将任何内容发送到服务器,服务器与Dropbox交互。这样,您就不会以任何格式分发私钥。

    一种可能的解决方案是在应用程序中对数据进行编码,并在运行时使用解码(当您想要使用该数据时)。我还建议使用progaurd,使您的应用程序的反编译源代码难以阅读和理解。例如,我在应用程序中放入一个编码密钥,然后在运行时在应用程序中使用解码方法解码我的密钥:

    // "the real string is: "mypassword" "; 
    //encoded 2 times with an algorithm or you can encode with other algorithms too
    public String getClientSecret() {
        return Utils.decode(Utils
                .decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
    }
    
    升级应用程序的反编译源代码如下:

     public String c()
     {
        return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
      }
    
    至少对我来说已经够复杂了。当我别无选择只能在应用程序中存储值时,我就是这样做的。当然,我们都知道这不是最好的方法,但对我来说很有效

    /**
     * @param input
     * @return decoded string
     */
    public static String decode(String input) {
        // Receiving side
        String text = "";
        try {
            byte[] data = Decoder.decode(input);
            text = new String(data, "UTF-8");
            return text;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "Error";
    }
    
    反编译
    /**
     * @param input
     * @return decoded string
     */
    public static String decode(String input) {
        // Receiving side
        String text = "";
        try {
            byte[] data = Decoder.decode(input);
            text = new String(data, "UTF-8");
            return text;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "Error";
    }
    
     public static String a(String paramString)
      {
        try
        {
          str = new String(a.a(paramString), "UTF-8");
          return str;
        }
        catch (UnsupportedEncodingException localUnsupportedEncodingException)
        {
          while (true)
          {
            localUnsupportedEncodingException.printStackTrace();
            String str = "Error";
          }
        }
      }
    
    GoolgeAPIKey = "Your API/Secret Key"
    
    buildTypes.each {
        it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
    }
    
    BuildConfig.GoogleSecAPIKEY
    
    $ strings libsecretlib.so | grep My
      My_S3cr3t_P@$$W0rD