存储API密钥

6
我正在使用Goodreads API获取React native应用程序的书籍数据。我必须使用一个密钥来使用该API。我是否可以将API密钥存储在应用本身上,还是应该将密钥放在服务器上,然后将所有数据重定向到应用程序?

你的项目是如何创建的? 你使用了 Expo 还是 React Native Init? - SHG21
@SHG21 react-native init - Nadim Ahmed
@SHG21 那么它在生产环境中会安全吗?因为密钥仍然存储在客户端应用程序上。 - Nadim Ahmed
API密钥始终可以从客户端应用程序中提取,无论它们是如何到达的,因此您的API的安全性不能依赖于API密钥的保密性(这似乎与直觉相反,但这是事实)。因此,请不要将它们视为敏感值,并且不要使您的API依赖于此类密钥的保密性。底线:在客户端上存储API密钥。 - TheGreatContini
3个回答

11
我在文章如何通过静态二进制分析从移动应用中提取API密钥中演示了它可以通过几个开源工具进行提取,例如使用Mobile Security Framework,但您也可以使用MiTM攻击来获取API密钥,正如我在文章使用中间人攻击窃取API密钥中所示,该攻击使用了开源工具MiTM Proxy。因此,不建议将第三方API密钥存储在移动应用中,因为攻击者可以轻松地获取并使用它们,这可能导致第三方供应商的账单飙升,直到您意识到问题存在时才能撤销API密钥,关闭移动应用的使用。如果您发布一个新版本的移动应用程序,并使用新的API密钥,攻击者很快就会回来并再次窃取API密钥。因此,建议将API密钥放置在一个服务器上,然后将所有数据重定向到应用程序。

是的,这是一个好方法,因为现在你只需要一个地方来存储和保护所有第三方API密钥。这有利于让您根据自己的意愿控制和限制它们的使用。

使用这种解决方案,您仍然需要在移动应用程序中使用API密钥才能访问API服务器,但是尽管您继续容易受到攻击者窃取,但现在您可以直接控制对API服务器的访问并且如果您确定每次访问API服务器的WHOWHAT,那么您将拥有更精细的控制,但攻击者仍将能够在我们所有的防御之间滑动,因为很难知道WHAT正在访问API服务器。

您现在可能在想...您介意解释一下WHOWHAT之间的区别吗?

访问API服务器时WHOWHAT的区别

为了更好地理解访问API服务器时WHOWHAT之间的区别,让我们使用这张图片:

Man in the Middle Attack

预期的通信渠道表示使用未被篡改的移动应用程序,由合法用户直接与API服务器通信,并且没有中间人攻击。实际通信渠道可能涉及多种不同的情况,如使用重新打包的移动应用程序的合法用户具有恶意意图,黑客使用真正的移动应用程序并进行中间人攻击,以了解移动应用程序和API服务器之间的通信方式,以便能够自动化攻击API。现在你可能已经明白为什么WHO和WHAT不是相同的了。WHO是移动应用程序的用户,我们可以使用OpenID Connect或OAUTH2流等多种方式对其进行身份验证、授权和识别。

OAUTH

OAuth通常为客户端提供了代表资源所有者对服务器资源的“安全委托访问”。它规定了一种过程,使资源所有者能够授权第三方访问其服务器资源,而无需共享其凭据。OAuth专门设计用于与超文本传输协议(HTTP)配合使用,基本上允许授权服务器为第三方客户端发放访问令牌,经过资源所有者的批准后。然后第三方使用访问令牌访问由资源服务器托管的受保护资源。

OpenID Connect

OpenID Connect 1.0是OAuth 2.0协议之上的一个简单身份验证层。它允许客户端根据授权服务器执行的身份验证来验证终端用户的身份,并以可互操作和类REST方式获取有关终端用户的基本配置文件信息。

虽然用户认证可能会让API服务器知道WHO正在使用API,但它不能保证请求来源是否符合预期,即移动应用程序的原始版本WHAT

现在我们需要一种方法来识别调用API服务器的WHAT,这比大多数开发人员想象的要棘手得多。 WHAT 是向API服务器发出请求的东西。它是否真的是移动应用程序的真实实例,还是一个机器人、自动脚本或攻击者手动使用像Postman这样的工具来探测API服务器?

为了让你惊喜,你可能会发现有人使用重新打包的移动应用程序或自动脚本来玩游戏并利用该应用程序提供的服务,而他们可能是合法用户之一。
开发人员通常会在其移动应用程序的代码中硬编码一个API密钥来识别“什么”。一些开发人员会额外努力,在移动应用程序中运行时计算密钥,因此它成为运行时秘密,而不是以前嵌入代码中的静态秘密。
上面的文字摘自我写的一篇文章,标题为“为什么您的移动应用程序需要API密钥?”,您可以在这里阅读完整的文章,这是关于API密钥的一系列文章中的第一篇。

您的问题尚未解决

现在您知道了谁和什么正在访问您的API服务器之间的区别,您必须已经意识到您的API服务器仍然容易受到攻击者的滥用。
您现在可以采取多层防御措施,从reCaptcha V3开始,接着是Web Application Firewall(WAF),最后如果您负担得起,则使用User Behavior Analytics(UBA)解决方案。

谷歌reCAPTCHA V3:

reCAPTCHA是一个免费的服务,可保护您的网站免受垃圾邮件和滥用。reCAPTCHA使用先进的风险分析引擎和自适应挑战来防止自动软件在您的网站上从事滥用活动。它可以让您的有效用户轻松通过。

...帮助您检测网站上的滥用流量,而无需任何用户摩擦。它基于与您的网站的交互返回一个分数,并为您提供更多灵活性以采取适当的行动。

WAF - Web应用程序防火墙:

Web应用程序防火墙(或WAF)过滤、监视和阻止与Web应用程序之间的HTTP流量。WAF不同于常规防火墙,因为WAF能够过滤特定Web应用程序的内容,而常规防火墙则作为服务器之间的安全门。通过检查HTTP流量,它可以防止源于Web应用程序安全漏洞的攻击,例如SQL注入、跨站脚本(XSS)、文件包含和安全配置错误。

UBA - 用户行为分析:

根据Gartner的定义,用户行为分析(UBA)是一种关于检测内部威胁、有针对性的攻击和金融欺诈的网络安全过程。UBA解决方案查看人类行为模式,然后应用算法和统计分析来检测这些模式中的有意义的异常--表示潜在威胁的异常。UBA跟踪系统用户而不是设备或安全事件。像Apache Hadoop这样的大数据平台通过允许它们分析数百万字节的数据来检测内部威胁和高级持续性威胁,从而增加了UBA功能。
所有这些解决方案都基于负面识别模型工作,换句话说,它们尽力通过识别什么是坏的而不是什么是好的来区分好坏,因此它们容易出现误报,尽管其中一些使用了先进的技术,如机器学习和人工智能。
因此,您可能经常发现自己需要放松对API服务器访问的阻止方式,以便不影响好用户。这也意味着这些解决方案需要不断监视,以验证误报不会阻止您的合法用户,并且同时它们可以适当地将未经授权的用户保持在远离。
关于为移动应用提供API,可以使用积极识别模型通过使用Mobile App Attestation解决方案,该解决方案保证向API服务器发送的请求是可信的,而不会出现误报。
移动应用程序认证
使用移动应用程序认证解决方案使API服务器能够知道它只接收来自真实移动应用程序的请求。
移动应用认证服务的作用是通过在后台运行SDK与云端服务通信,保证运行时您的移动应用程序没有被篡改或不在根设备上运行。成功认证移动应用程序的完整性后,会发出一个短暂的JWT令牌,并使用API服务器和云端移动应用认证服务才知道的秘密进行签名。如果移动应用程序认证失败,则JWT令牌将使用API服务器不知道的秘密进行签名。现在,每个API调用都必须在请求头中发送JWT令牌。这将使API服务器能够仅在验证JWT令牌中的签名和过期时间时才提供请求,并在验证失败时拒绝它们。一旦移动应用程序认证服务使用的秘密未知于移动应用程序,则即使在应用程序被篡改、在根设备上运行或与中间人攻击的目标通信时,也无法在运行时对其进行反向工程。移动应用认证服务已经作为SAAS解决方案存在于Approov(我在这里工作),为多个平台提供SDK,包括iOS、Android、React Native等。集成还需要在API服务器代码中进行小的检查,以验证云服务发出的JWT令牌。这个检查对于API服务器能够决定哪些请求提供,哪些请求拒绝是必要的。

任何在客户端运行并需要某些秘密访问API的东西都可以以不同的方式被滥用,您必须将所有第三方API的访问委托给您控制的后端,以此来减少攻击面,同时保护它们的秘密免受公众窥视。

最终,为了保护您的API服务器,应选择与您试图保护的价值和该类型数据的法律要求相符的解决方案,例如欧洲的GDPR法规。


感谢您的详细回复和提供替代方法。我一定会查看您的服务。 - Ed Barahona

1
使用react-native-config库来进行React Native开发。使用该库可以保护API密钥,同时可以保存在本地代码中使用的更多秘密密钥,如onesignal、codepush等。请注意保留HTML标签。

https://github.com/luggit/react-native-config


这样做不起作用,因为它将字符串中的所有数据都取出来,所以无法找到... - Wasi Sadman
请注意,该模块不会对打包的密钥进行混淆或加密,因此不要将敏感密钥存储在 .env 文件中。基本上不可能防止用户逆向工程移动应用程序密钥,因此设计您的应用程序(和 API)时要考虑到这一点。所以,这不适合生产代码,唯一的存储方式是在后端服务器中。 - Abdullah Khaled

-2
将它们存储在一个名为.env的文件中,格式如下:API_KEY=yourKey
安装npm包react-native-dotenv。
然后使用react-native-dotenv包根据需要导入文件; import { API_KEY } from 'react-native-dotenv'
.env文件不应该被提交到Github。

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