在安卓(Flutter)应用中安全地保存API密钥

16

我知道这个问题已经被问过很多次了,我都看过了。我在Google上搜索过,但仍有一些问题无法找到答案。

目前,我正在我的应用程序中存储API密钥。这是一个不好的做法。根据我的了解,我可以使用ProGuard或DexGuard进行混淆。我还可以使用Keystore来安全地存储我的API密钥。现在,这里是我的问题: - 混淆会更改变量和类的名称。当某人反编译apk文件时,我的API密钥仍然会存在于该应用程序中。当然,这可能需要更多的时间,但需要多长时间呢?例如20分钟?我觉得关键是只要他们花费一些时间和精力,他们就可以使用这个密钥。我弄错了吗?

另一个我在不同网站上看到的答案是,我可以将我的密钥存储在服务器上,然后通过我的应用程序传递它。

  • 这怎么可能呢?服务器将向应用程序发送API密钥。如果黑客找到了这个URL,他们可以打开它并获取密钥。如果我使用密钥访问URL,那么我就陷入了一个永无止境的密钥循环中。我该如何处理?

  • 有人说我可以加密我的密钥,然后在应用程序中解密它。但是人们不能反编译我的解密函数并找出我的密钥吗?

我也被告知Firebase远程配置将成为存储我的密钥的安全方法。但又出现了另一个问题。

  • 这种方法有多安全?
  • 如果我正在使用 Google 服务的 JSON 文件来识别我的项目,难道不是任何人都可以从远程配置部分获取我的密钥吗?因为我在控制台上看不到任何有关远程配置的设置,以便确定谁可以访问它,谁不能。我应该如何安全地存储 Firebase 上的 API 密钥?

  • 黑客难道不只是需要反编译 APK,然后改变代码并从我的 Firebase 帐户中提取数据吗?因为 Google 服务的 JSON 文件就在那里。如果他们打印提取的数据,他们能够访问所有内容吗?

那么,我应该采取什么样的措施才能安全地为第三方应用程序使用 API 密钥?而其中一些 API 密钥非常有价值,而另一些只是从其他服务器获取信息。我只想知道最安全的方法来存储这些密钥。

3个回答

29

提取API密钥有多难?

当有人反编译apk文件时,我的API密钥仍然会在该应用程序中。当然,这可能需要更长的时间,但需要多长时间呢?例如20分钟?我觉得关键是,只要他们付出一些时间和努力,他们就可以使用这个密钥。我理解得对吗?

你说例如20分钟?... 这取决于你是否已经在电脑上安装了工具,但如果你至少安装了Docker,你可以利用一些惊人的开源工具,在不到20分钟的时间内从中提取API密钥,也许只需大约5分钟,继续阅读以了解如何做到。

使用静态二进制分析提取API密钥

你可以阅读我的文章如何通过静态二进制分析从移动应用程序中提取API密钥,在那里你将学习如何在不到五分钟的时间内完成此操作,而无需任何先前的黑客知识。以下是引用:

I will now show you a quick demo on how you can reverse engineer an APK with MobSF in order to extract the API Key. We will use the MobSF docker image, but you are free to install it in your computer if you wish, just follow their instructions to do it so.

To run the docker image just copy the docker command from the following gist:

#!/bin/bash

docker run -it --name mobsf -p 8000:8000 opensecurity/mobile-security-framework-mobsf
所以,当docker容器启动并运行后,您只需要访问http://localhost:8000并在Web界面中上传您的移动应用程序二进制文件,等待MobSF为您完成所有繁重的工作。
现在,如果您的API密钥隐藏在本机C / C ++代码中,则上述方法将无法正常工作,如我在同一篇文章中所述:
到目前为止,我们唯一找不到的API密钥是来自C ++本地代码的JNI_API_KEY,这并不容易做到,因为C ++代码编译成HEX格式的.so文件,并且不包含任何对JNI_API_KEY的引用,因此很难将字符串与它们所属的内容连接起来。
但是不要担心,您可以使用中间人(MitM)攻击或仪表化框架来提取API密钥。
使用MitM攻击提取API密钥
只需按照我的文章Steal that API Key with a Man in the Middle Attack在您可以控制的设备中提取它:
为了帮助演示如何窃取API密钥,我已经构建并发布了Github上的Currency Converter Demo Android应用程序,该应用程序使用了我们早期Android Hide Secrets应用程序中使用的相同JNI / NDK技术来隐藏API密钥
因此,在本文中,您将学习如何设置和运行MitM攻击,以拦截您控制的移动设备上的https流量,以便您可以窃取API密钥。最后,您将了解到高层次上如何缓解MitM攻击。
但是您可能会说您使用证书固定,因此MitM攻击将无法正常工作。如果是这样,我邀请您阅读我的文章Byapssing Certificate Pinning
之前的一篇文章中,我们了解了如何使用证书固定保护移动应用和API服务器之间的https通信渠道,正如承诺的那样,在该文章结尾,我们现在将看到如何绕过证书固定。为了演示如何绕过证书固定,我们将使用与上一篇文章中相同的货币转换器演示移动应用程序。在本文中,您将学习如何重新打包移动应用程序以使其信任自定义ssl证书。这将允许我们绕过证书固定。

使用Instrumentation Framework提取

因此,如果以上方法都不适用于您,则可以使用一个Instrumentation Framework,例如广泛使用的Frida

将您自己的脚本注入黑盒进程中。钩住任何函数,监视加密API或跟踪私有应用程序代码,无需源代码。编辑,保存,立即查看结果。所有这些都不需要编译步骤或程序重新启动。

所以无论您最终做什么,移动应用程序中的秘密始终可以被提取出来,这只取决于攻击者的技能水平以及他愿意付出的时间和精力。

在移动应用程序中加密存储API密钥?

有人说我可以加密我的密钥,然后在接收到应用程序后解密它们。

因此,您可以使用Android Hardware-backed Keystore

系统芯片(SoC)中可信执行环境的可用性为Android设备提供了一个机会,以向Android OS、平台服务甚至第三方应用程序提供硬件支持的强大安全服务。

在某些时候,从这个keystore检索到的秘密将需要用于发出http请求,在这一点上,攻击者所需做的就是在返回时挂钩一个Instrumentation Framework到返回API密钥解密的函数调用上来提取它。 而要找到解密函数,攻击者所需要做的就是反编译您的APK并像您已经想到的那样找到它:

但是,人们不能反编译我的解密函数并找出我的密钥吗?

FIREBASE和SAFETYNET拯救了吗?

我也被告知Firebase Remote Config将是存储我的密钥的安全方法。

再次,攻击者所需要做的就是使用Instrumentation Framework从任何他确定为使用Firebase配置的函数中提取所需的所有内容。

如果你认为Firebase和/或你的移动设备受到SafetyNET保护,那么我需要提醒你的是,根据Google自己的声明,SafetyNet检查运行移动应用程序的设备的完整性,而不是移动应用程序本身的完整性:

此API的目标是为您提供有关运行您的应用程序的设备完整性的信心。然后,您可以使用标准Android API获取其他信号。您应将SafetyNet Attestation API用作反滥用系统的深度防御信号的一部分,而不是应用程序的唯一反滥用信号。

此外,我建议您阅读我在回答中提供的内容,以了解开发人员在实现安全网络时需要注意什么。

因此,尽管SafetyNet是Android安全生态系统的非常好的改进,但它并不是设计用于作为独立的防御措施,也不能保证移动应用程序没有被篡改,对此,您需要使用Mobile App Attestation概念。

代理或后端服务器

现在,我在不同网站上看到的另一个答案是,我可以将我的密钥存储在服务器上,然后通过我的应用程序进行通信。

这怎么可能?服务器将向应用程序发送API密钥。如果黑客找到此URL,他们可以打开它并获取密钥。如果我使用一个密钥访问URL,那么我就会进入一个永无止境的密钥循环。我该如何做到这一点?

虽然你可能会说这只是将问题从移动应用程序转移到代理或后端服务器,但我必须说至少代理或后端服务器是你可控制的东西,而移动应用程序不是。任何下载它的人都可以随心所欲地使用它,你无法直接控制它,你只能在APK中添加尽可能多的屏障,使其变得困难。

我建议您阅读我在回答中提供的内容,以了解为什么不应尝试在移动应用程序中保护API密钥,而应将它们移动到后端或代理服务器中。

可能更好的解决方案

那么,我到底应该怎么做才能安全地为我的第三方应用程序使用API密钥?其中一些API密钥非常有价值,而另一些则只是从其他服务器获取信息。我只想知道存储这些密钥的最安全方法。

我能给你的最好建议是阅读我的答案,了解如何确实在移动应用程序中摆脱API密钥,并使后端对请求的起源确实来自于您的移动应用程序实例具有高度信心。

你想走得更远吗?

在回答安全问题时,我总是喜欢引用OWASP基金会的出色工作。

对于移动应用程序

OWASP移动安全项目-前10个风险

OWASP移动安全项目是一个集中的资源,旨在为开发人员和安全团队提供构建和维护安全移动应用程序所需的资源。通过该项目,我们的目标是分类移动安全风险并提供开发控件以减少其影响或被利用的可能性。

OWASP-移动安全测试指南

移动安全测试指南(MSTG)是一本全面的手册,用于移动应用程序的安全开发、测试和逆向工程。

对于API

OWASP API安全前10名

OWASP API安全项目旨在通过强调不安全API中的潜在风险,并说明如何减轻这些风险,为软件开发人员和安全评估者提供价值。为了实现这个目标,OWASP API Security项目将创建和维护前10个API Security Risks文档,以及一个最佳实践的文档门户,用于创建或评估API。


5
如果您认为API密钥不应被泄露,那么就不应将其放在应用程序中。您可以使用以下可能的解决方案:
  1. 您可以将密钥保存在服务器上,并通过您的服务器路由需要该密钥的所有请求。因此,只要您的服务器是安全的,那么您的密钥也是安全的。当然,这种解决方案会带来性能成本。您可以使用SSL pinning来验证响应。请参阅此处
  2. 您可以编程获取应用程序的签名密钥,并在每个API调用中发送它以验证该请求。但黑客可能会找出策略。
  3. Google不建议在远程配置中存储API密钥,但您可以在那里保留一个令牌并用其验证请求并发送API密钥。请参阅此处
  4. 对于Android应用程序,您可以使用Google的SafetyNet API验证应用程序的真实性,服务器可以在验证SafetyNet响应后为用户生成一个令牌。该令牌可以进一步用于验证请求。Flutter有一个插件可供使用SafetyNet API。
您可以结合上述方法以确保API密钥的安全性。回答您的问题,Firebase远程配置使用SSL连接传输数据,非常安全,但您不应该完全依赖它来保护数据安全。您也不能使用公共可访问的API共享API密钥。此外,在应用程序中同时存储加密密钥和数据并不能使其更安全。

我不知道该如何在服务器上储存我的API密钥。例如,我有一个从谷歌获取地理编码API的配额。假设我将其存储在服务器上,并且每当应用程序调用somedomain.com/geocoding时进行调用。黑客在这里并不需要拥有API密钥,但仍然可以使用我的服务器完成我的配额。我该如何防止这种情况发生? - SOMEGUY BUT ONEGUY
你做错了。你可以使用相同的Firebase项目API密钥来使用Google位置API。查看这个 - Sanjay Sharma
谢谢你的帖子。我还有一个问题:如果我使用Firebase身份验证并使用UID(用户ID)与我的服务器通信,这样安全吗?在这种情况下,没有用户会知道他们的UID,任何想要与服务器通信的人都无法在没有UID的情况下进行通信(使用HTTPS)。 - SOMEGUY BUT ONEGUY
在这种情况下,如果黑客通过 Firebase 绕过身份验证并找到 UUI,您的数据将面临风险,因此您还应该在服务器级别上进行适当的身份验证。 - Sanjay Sharma
有点蠢的问题,但我对安全方面还不太熟悉,服务器级别的适当身份验证应该是什么样子的?如果用户绕过 Firebase 的身份验证,我可以采取哪些措施来防止完全访问? - SOMEGUY BUT ONEGUY

0

您可以使用freeRASP来降低反向工程的风险,支持Android、iOS和Flutter。

高级计划提供更多保护功能,例如应用完整性密码术,以保护API免受应用程序冒充攻击,以及安全存储SDK,以保护静态资产。


虽然此链接可能回答了问题,但最好在此处包括答案的基本部分,并提供参考链接。仅有链接的答案如果链接页面发生更改,则可能变得无效。-【来自审查】 - Jagar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接