问答中心分类: ANDROID在应用程序中存储和保护私有API密钥的最佳实践
0
匿名用户 提问 1月 前

关闭.这个问题是基于意见的.它目前不接受答案。

想改进这个问题吗?更新问题,使其可以由编辑这篇文章.
关闭两年前.

改进这个问题

大多数应用程序开发人员会将一些第三方库集成到他们的应用程序中。如果是为了访问Dropbox或YouTube等服务,或是为了记录崩溃。第三方图书馆和服务的数量惊人。大多数库和服务都是通过某种方式与服务进行身份验证来集成的,大多数情况下,这是通过API密钥实现的。出于安全目的,服务通常会生成公共和私有密钥,通常也称为机密密钥。不幸的是,为了连接到服务,必须使用该私钥进行身份验证,因此可能是应用程序的一部分。
假设我有类似的东西,我如何保护密钥:

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

}

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

Patrick Dorn 回复 1月 前

我将其存储在image/png中,并作为BufferReader通过png获取密钥

Patrick Dorn 回复 1月 前

我认为这是一个合理的担忧,并在Firebase Android SDK github页面上发布了一个类似的问题:github。com/firebase/firebase安卓sdk/issues/1583.让我们看看这件事是否得到处理。

13 Answers
0
Eric Lafortune 回答 1月 前
  1. 实际上,编译后的应用程序包含密钥字符串,但也包含常量名称APP_key和APP_SECRET。从这种自文档代码中提取密钥非常简单,例如使用标准的Android工具dx。
  2. 你可以申请ProGuard。它会保持键字符串不变,但会删除常量名称。它还将尽可能使用简短、无意义的名称重命名类和方法。然后提取键需要更多的时间,以确定哪个字符串用于哪个目的。
    请注意,设置ProGuard不应该像您担心的那样困难。首先,您只需要启用ProGuard,如project中所述。财产。如果第三方库存在任何问题,您可能需要在proguard project中抑制一些警告和/或防止它们被混淆。txt。例如:

    -dontwarn com.dropbox.**
    -keep class com.dropbox.** { *; }

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

  3. 您可以在代码中手动混淆字符串,例如使用Base64编码或更复杂的东西;甚至可能是本地代码。然后,黑客将不得不对您的编码进行静态反向工程,或在适当的位置动态拦截解码。
  4. 你可以应用一个商业模糊器,比如ProGuard的特殊兄弟德克斯卫兵。它还可以为您加密/模糊字符串和类。然后提取密钥需要更多的时间和专业知识。
  5. 您可以在自己的服务器上运行部分应用程序。如果你能把钥匙放在那里,它们就安全了。

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

user4414636 回复 1月 前

这是第五点讨论的本地服务器吗?这不需要一台需要一直开机的电脑吗?对不起,我是新手。我能用类似Parse的东西吗?

Alan 回复 1月 前

@EricLafortune如果私钥字符串存储在Java类中与字符串资源XML中没有区别吗?

David Thomas 回复 1月 前

@EricLafortune现在可以使用Android密钥库系统安全地存储密钥了吗?(开发商安卓com/training/articles/keystore。html)

Sagar Trehan 回复 1月 前

@大卫托马斯:你试过使用密钥库吗。我想混淆用Java类编写的API密钥。请回复

David Thomas 回复 1月 前

@SagarTrehan不,我还没有尝试密钥库方法。这里有一个叫做“dexguard”的流行工具,你可能会对它感兴趣。

Weishi Z 回复 1月 前

安卓密钥库对此有用吗?(开发商安卓com/training/articles/…)

pete 回复 1月 前

我不明白。它的问题和原来的问题不一样吗?

Bart van Ingen Schenau 回复 1月 前

@pete:#5表示您拥有的服务器在身份验证中充当中间人。API密钥将只存储在您的服务器上,不会包含在任何脱离您控制的代码中。

Yann39 回复 1月 前

使用强密钥派生算法(例如bcrypt、argon2等)将密钥存储在数据库(例如嵌入式sqlite)中如何?它会不会像在其他地方(甚至在服务器上)保存它们一样,因为获取它们的唯一方法是运行代码并在适当的位置拦截解码?

sudocoder 回复 1月 前

@Bart van ignen Schenau我和Pete有同样的问题。如何防止有人对你的应用程序进行反编译并查看function()的工作原理。然后,他们可以复制算法,并与您的服务器通信,以执行不好的操作。

Bart van Ingen Schenau 回复 1月 前

@sudocoder:当然,服务器会验证请求是否来自经过身份验证的用户。

sudocoder 回复 1月 前

@BartvanIngenSchenau我如何向服务器发送请求以验证我是否确实通过了身份验证?我能想出一个解决办法。。。我会发送私钥凭证。。。但这不是我们试图解决的最初问题吗?

MEE 回复 1月 前

@sudocoder您要求用户提供登录凭据以进行身份验证。如果你的应用不需要用户/客户端身份验证,那么你可能只需要在后端服务器上使用速率限制和DoS防御。

Ben-J 回复 1月 前

我们提供了一个免费的开源Dexguard替代方案,可以在Android中深度隐藏密钥。这是一个gradle插件,使用NDK和XOR运算符来防止反向工程:github。com/klaxit/hidden secrets gradle插件

Carlos Iglesias 回复 1月 前

这个答案忽略了中间人的安全问题。

0
marcinj 回答 1月 前

很少有想法,在我看来只有第一个能提供一些保证:

  1. 把你的秘密保存在互联网上的某个服务器上,需要的时候就把它们拿出来使用。若用户即将使用dropbox,那个么并没有什么能阻止你们向你们的网站提出请求并获取你们的密钥。
  2. 把你的秘密放在jni代码中,添加一些可变代码,使你的库更大,更难反编译。您还可以将密钥字符串拆分为几个部分,并将它们保存在不同的位置。
  3. 使用模糊处理程序,也可以将代码放入哈希机密中,然后在需要时将其解压。
  4. 将你的密钥作为你的一张图片在资产中的最后一个像素。然后在需要的时候在代码中阅读它。混淆代码应该有助于隐藏将读取它的代码。

如果您想快速了解阅读apk代码有多容易,请抓取APKAnalyser:
http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/

Matt Wolfe 回复 1月 前

如果用户可以反编译应用程序,尽管他们可能会确定对您自己的服务器发出的请求,并简单地执行该请求以获取机密。这里没有银弹,但走几步,我打赌你会没事的!如果你的应用非常受欢迎,但可能不是。。好主意!

marcinj 回复 1月 前

是的,1号不能保证。

Igor Čordaš 回复 1月 前

我真的很喜欢在图像中隐藏密钥的想法+1.

Sanandrea 回复 1月 前

我更喜欢2号,看看:github。com/sanandrea/CSecretKey等待一些意见/建议

Dr.jacky 回复 1月 前

@MarcinJędrzejewski您想进一步解释(最好是用示例或剪接的代码)关于forth解决方案吗?非常感谢。

marcinj 回复 1月 前

@海德先生,这就是所谓的隐写术,它的方式太复杂,无法在这里给出示例代码,你可以在谷歌上找到示例。我在这里找到了一个:dreamincode。net/forums/topic/27950隐写术这个想法很好,但因为apk代码可以反编译,它破坏了它的美。

Dr.jacky 回复 1月 前

@MarcinJędrzejewski有趣的是,隐写术的算法太多了。你知道有什么工具可以同时在windows操作系统和android上使用吗?我想在PC中对消息进行编码,并在我的android应用程序中使用Decision方法。

Mohsen Mirhoseini 回复 1月 前

我真的很喜欢4号。👍

Sumit 回复 1月 前

是的,我已经存储在图像文件中,并通过图像作为缓冲读取器获取密钥,虽然不安全,但很好用

pete 回复 1月 前

#1有什么帮助?攻击者只需在查询主服务之前查询服务器上的密钥,就可以像以前一样轻松地模拟你的应用,不是吗?

pete 回复 1月 前

#3是如何工作的?你怎么“不高兴”

sudocoder 回复 1月 前

如果我错了,请纠正我,#1有效,因为您登录并获得会话令牌。然后你用这个会话令牌通过你的服务器进行每一次呼叫——服务器为你获取并返回信息。这意味着,如果我想调用dropbox的api,我必须先用我授权的会话令牌通过我的服务器。

marcinj 回复 1月 前

@sudocoder并不是真的,它在评论中解释道——这个想法是关于通过https连接使用对服务器的请求来获取api密钥(或任何其他机密)。这样你就不会在apk中保密,但正如评论中所解释的,黑客可能会发现http请求并尝试调用它。这就是安全性超越了“开始”的原因:-你可以用某种形式的SiggoGrAffy在图像中打包你的秘密,并在原生C++代码中解码它。尽管如此,聪明的黑客还是会在dropbox请求形成之前简单地打印出这个密钥——在所有这些淫秽之后。。。。

marcinj 回复 1月 前

@sudocoder您关于使用自己的服务器作为dropbox代理的想法似乎很好,api密钥将以这种方式得到保护——但这可能会使dropbox api的使用变得复杂。最近,我发现与黑客斗争很困难,对我来说,有效的方法是分析黑客在apk中改变了什么以破坏安全性,并在这些地方进行修复以保护他们。这需要几个小时,也许一天。经常发布更新的apk,确保你的内容只适用于最新版本(或两个最新版本)使盗版者不能使用旧的破解版本。

0
Skip Hovsmith 回答 1月 前

另一种方法是一开始就不要把秘密放在设备上!看见移动API安全技术(特别是第三部分)。
使用历史悠久的间接寻址传统,在API端点和应用程序身份验证服务之间共享秘密。
当你的客户想要API调用,它要求app auth服务对其进行身份验证(使用强大的远程认证技术),并收到一个时间限制(通常为JWT)由秘密签名的令牌。
令牌将随每个用户一起发送API调用端点可以在对请求采取行动之前验证其签名。
真正的秘密永远不会出现在设备上;事实上,应用程序永远不知道它是否有效,它会突出请求身份验证并传递生成的令牌。作为间接寻址的一个很好的好处,如果你想改变秘密,你可以不要求用户更新他们安装的应用程序。
因此,如果你想保护你的秘密,一开始不在应用程序中使用它是一个很好的方法。

ortonomy 回复 1月 前

这应该是公认的答案。

Ashi 回复 1月 前

当您想要访问身份验证服务时,问题仍然存在。它会给你一个客户id和客户机密。我们应该在哪里拯救他们?

Kibotu 回复 1月 前

不解决需要首先对api进行身份验证才能使用的私有api。你从哪里获得所有应用程序用户的凭据?

Simranjeet Singh 回复 1月 前

@ASI客户端id在某种程度上是模糊的,只有api端点知道如何从模糊数据中提取数据,例如,如果只有客户端id的一些字符(其中客户端id是实际的客户端id+一些用于创建模糊字符串的数据)仅指实际数据,但如果黑客试图更改或更改客户端id,他不知道哪些数据实际上代表客户id,因为实际上只有api端点知道客户id是如何被混淆的,以及如何从实际代表客户id的客户id中提取有用的数据。。。。希望你明白我的意思

Simranjeet Singh 回复 1月 前

这肯定是可以接受的答案,因为它很容易理解和实现

0
SANAT 回答 1月 前

老办法:
按照3个简单步骤保护API/密钥(老生常谈)
我们可以使用Gradle来保护API密钥或密钥。
1.格拉德尔。物业(项目物业):使用键创建变量。

GoolgeAPIKey = "Your API/Secret Key"

2.建造。gradle(模块:应用程序):在构建中设置变量。gradle在活动或片段中访问它。将下面的代码添加到buildTypes{}。

buildTypes.each {
    it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}

3.通过应用程序的BuildConfig在活动/片段中访问它:

BuildConfig.GoogleSecAPIKEY

更新:
上述解决方案有助于通过Git提交开源项目。(感谢戴维·罗森和里亚兹·阿里的评论)。
根据Matthew和Pablo Cegarra的评论,上述方法不安全,反编译器将允许有人使用我们的密钥查看BuildConfig。
解决方案:
我们可以使用NDK来保护API密钥。我们可以将密钥存储在本机C/C++类中,并在Java类中访问它们。
请跟我来this博客使用NDK保护API密钥。
关于如何在Android中安全存储代币的后续研究

Google 回复 1月 前

在gradle文件中存储密钥是安全的吗?

David Rawson 回复 1月 前

@谷歌gradle.properties不应该签入Git,所以这至少是一种对提交的源代码保密的方法

riyaz-ali 回复 1月 前

这并不能阻止API密钥被捆绑到生成的应用程序中apk(它将被添加到生成的BuildConfig虽然这对于管理不同的API密钥(例如,在开源项目中)绝对是个好主意

Matthew 回复 1月 前

使用Java反编译器将允许用户查看BuildConfig文件和“GoogleSecAPIKEY”

iceman 回复 1月 前

你的BuildConfig.java文件将以纯文本形式提供密钥。这并不比OP已经在做的更好。

mtrewartha 回复 1月 前

如前所述,如果解决方案明显不安全,那么答案顶部非常大的粗体文本可能不应该说“安全”。我知道你有一个“更新”部分,但很多人可能不读。

Ben-J 回复 1月 前

关于NDK解决方案,我们创建了一个免费的GADLE插件,将你的密钥混淆并存储在C++文件中,可以从java代码中访问。所有细节如下:github。com/klaxit/hidden secrets gradle插件

0
Ayman Al-Absi 回答 1月 前

添加到@Manohar Reddy solution时,可以使用firebase数据库或firebase RemoteConfig(默认值为空):

  1. 加密你的钥匙
  2. 将其存储在firebase数据库中
  3. 在应用程序启动期间或需要时获取
  4. 破译钥匙并使用它

这个解决方案有什么不同?

  • firebase没有credintials
  • firebase访问受保护,因此只有具有签名证书的应用才能访问
  • 加密/解密以防止中间人拦截。然而
Ashi 回复 1月 前

尽管大家都尊重这个解决方案,但我们仍然是第一方。建议使用证书,而不是使用凭据。任何能够窃取您的凭据的人都能够窃取您的签名证书。

Ashi 回复 1月 前

不过,有一个优点是,通过建议的解决方案,我们在黑客面前又增加了一个复杂性。

Code Name Jack 回复 1月 前

我们从不把私人证书保存在源代码中,所以没有偷窃的可能,对吗?