移动应用程序中的 OAuth 机密

Posted

技术标签:

【中文标题】移动应用程序中的 OAuth 机密【英文标题】:OAuth secrets in mobile apps 【发布时间】:2010-12-28 09:32:42 【问题描述】:

使用 OAuth 协议时,您需要从您要委托的服务中获取一个秘密字符串。如果您在 Web 应用程序中执行此操作,您可以简单地将秘密存储在您的数据库或文件系统中,但是在移动应用程序(或桌面应用程序)中处理它的最佳方法是什么?

将字符串存储在应用程序中显然不好,因为有人很容易找到并滥用它。

另一种方法是将其存储在您的服务器上,并让应用程序在每次运行时获取它,而不是将其存储在手机上。这几乎同样糟糕,因为您必须在应用程序中包含 URL。

我能想到的唯一可行的解​​决方案是首先正常获取访问令牌(最好使用应用程序内的网络视图),然后通过我们的服务器路由所有进一步的通信,这会将秘密附加到请求中数据并与提供者通信。再说一次,我是一个安全菜鸟,所以我真的很想听听一些知识渊博的人对此的看法。在我看来,大多数应用程序都不会用这些长度来保证安全性(例如,Facebook Connect 似乎假设您将秘密放入应用程序中的字符串中)。

另一件事:我不认为最初请求访问令牌涉及秘密,因此可以在不涉及我们自己的服务器的情况下完成。我说的对吗?

【问题讨论】:

对不起,如果我没有明白这一点,但是在应用程序的数据库中存储代码有什么问题?因为这些令牌是在用户对其帐户进行身份验证后生成和存储的,所以可以安全地假设所述用户希望移动设备存储访问权限以进行访问。 即使用户已授权您访问他们的帐户(例如在 Twitter 上),您也必须使用从您尝试访问的服务中获得的密钥。此密钥与身份验证密钥和其他一些密钥一起用于与其服务器的所有通信。所以是的,您可以存储访问密钥,但不应存储秘密,因为它可以与 any 身份验证密钥一起使用来滥用服务。同样,我很乐意得到对此了解更多的人的纠正。 OAuth 提供了一种身份验证方法来保护原始用户的登录数据。为了实现这一点,生成了一个新的 unique 登录组合,该组合只与 unique 应用程序的组合键一起使用。与存储用户登录数据相比,最大的好处是这些数据首次授权之后是完全安全的,并且在任何违规情况下,用户都可以简单地撤销授权的访问权限。当然,不保存秘密是没有意义的,因为用户需要重新进行身份验证(这不是用户在授予应用程序访问权限时想要的)。 @poke 应该保存用户通过提供商批准您的应用程序时获得的身份验证密钥,但您在发布应用程序之前从提供商收到的秘密令牌不应该(在桌面或移动应用程序;如果是网络应用程序,您显然可以将密钥存储在服务器上,如问题中所述)。 根据我对 oAuth 的理解——如果是桌面应用程序,使用 ieinspector.com/httpanalyzer/index.html 这样的工具很容易嗅探/监控 HTTP/HTTPS 流量,因此您的令牌和令牌秘密都可以很容易找到。因此,唯一的保护是您的消费者机密。现在,如果您将秘密存储在应用程序中并且有人能够找到它,那么将任何其他应用程序冒充为您的应用程序就变得很容易了。如果我错了,请纠正我。 【参考方案1】:

是的,这是我们自己面临的 OAuth 设计的问题。我们选择通过我们自己的服务器代理所有呼叫。 OAuth 在桌面应用程序方面并未完全淘汰。在不更改 OAuth 的情况下,我发现的问题没有完美的解决方案。

如果您考虑一下并问我们为什么有秘密的问题,主要是为了提供和禁用应用程序。如果我们的秘密被泄露,那么提供商只能真正撤销整个应用程序。由于我们必须在桌面应用程序中嵌入我们的秘密,我们有点搞砸了。

解决方案是为每个桌面应用设置不同的密码。 OAuth 不会让这个概念变得简单。一种方法是让用户自己创建一个秘密,然后自己将密钥输入到您的桌面应用程序中(一些 Facebook 应用程序在很长一段时间内都做了类似的事情,让用户去创建 Facebook 来设置他们的自定义测验和废话)。这对用户来说不是很好的体验。

我正在为 OAuth 的委托系统提出建议。这个概念是,使用我们从提供商那里获得的我们自己的密钥,我们可以向我们自己的桌面客户端(基本上每个桌面应用程序一个)发布我们自己的委托密钥,然后在身份验证过程中我们将该密钥发送到顶层回拨给我们并与我们重新验证的提供商。这样我们就可以撤销我们发布给每个桌面客户端的自己的秘密。 (从 SSL 中借鉴了很多它的工作原理)。整个系统对于增值网络服务以及将调用传递给第三方网络服务都是完美的。

如果***提供商提供 API 来生成和撤销新的委托机密,则该过程也可以在没有委托验证回调的情况下完成。 Facebook 通过允许 Facebook 应用程序允许用户创建子应用程序来做类似的事情。

网上有一些关于这个问题的讨论:

http://blog.atebits.com/2009/02/fixing-oauth/ http://groups.google.com/group/twitter-development-talk/browse_thread/thread/629b03475a3d78a1/de1071bf4b820c14#de1071bf4b820c14

Twitter 和 Yammer 的解决方案是身份验证 pin 解决方案: https://dev.twitter.com/oauth/pin-based https://www.yammer.com/api_oauth_security_addendum.html

【讨论】:

这很有趣,尽管它证实了我的担心,即 OAuth 对于桌面/移动应用程序来说并不是那么好。当然,攻击者必须先获取秘密,然后再嗅探某人的凭据,因此需要相当大的决心。 pin 解决方案适用于桌面,但对于移动 imo 来说是笨拙的。 您提出的方案将如何帮助增值 Web 服务,因为这个问题不适用于它们?另外,我看不出它如何与生成新秘密的提供者一起工作,因为你需要一个“主秘密”来请求这些新秘密,所以你至少需要一次调用你自己的服务器(它保存主要秘密)。但这当然比通过您自己的服务器路由 all 流量要好。欢迎澄清!随着您的提案进展,请在此处更新! 只是好奇:你如何确定调用你的代理服务器的东西是合法的? 回应 notJim:让您的消费者秘密泄露的主要风险是可以使用它开发恶意(或愚蠢)应用程序,从而损害您的声誉并增加您拥有合法应用程序的风险因 API 滥用/误用而关闭。通过通过您控制的 Web 应用程序代理所有需要您的秘密的调用,您可以在您使用的 API 决定关闭之前查看滥用模式并撤销用户或访问令牌级别的访问权限关闭您的整个服务。 我同意这里的 quasistoic,您需要使用启用 SSL 的浏览器来处理 oauth 调用。这是一件好事,有几个原因,包括将来轻松管理任何安全更新,并且实际应用程序中的任何内容都不需要随着时间的推移进行更新。 Zac 指出 Twitter 提出了一个 PIN 解决方案,我实际上也想到了这个解决方案,因为你不能相信应用程序可以安全地获取代码。我建议使用带有现代加密的“Nonce”以及 PIN 和秘密来通过 Web 服务器代理请求。【参考方案2】:

正如其他人所提到的,将密钥本地存储在设备上应该没有真正的问题。

除此之外,您始终可以依赖基于 UNIX 的 android 安全模型:只有您的应用程序才能访问您写入文件系统的内容。只需将信息写入您应用的默认 SharedPreferences 对象即可。

为了获得秘密,必须获得对 Android 手机的 root 访问权限。

【讨论】:

谁提到的?如果您的意思是 poke 的评论,请参阅我的答案,即秘密!= 身份验证密钥。后者可以安全存储,前者不能。我不了解 Android,但获得对 iPhone 的 root 访问权限一点也不难。请注意,该密钥在应用程序的所有实例上都是相同的,因此攻击者只需获得对一个二进制文件的访问权限。即使他们无法在设备上获得 root 访问权限,他们也可以获取其他文件中的二进制文件并从中提取秘密令牌。 只是添加它也很容易root android手机【参考方案3】:

我没有丰富的 OAuth 经验 - 但不是每个请求不仅需要用户的访问令牌,还需要应用程序使用者密钥和秘密?因此,即使有人偷了移动设备并试图从中提取数据,他们也需要应用程序密钥和机密才能真正做任何事情。

我一直认为 OAuth 背后的目的是让每个拥有 mashup 的 Tom、Dick 和 Harry 都不必明文存储您的 Twitter 凭据。我认为它很好地解决了这个问题,尽管它有局限性。此外,它的设计并没有真正考虑到 iPhone。

【讨论】:

你说得对,OAuth 在设计时主要考虑了 Web 应用程序,我相信它适用于此。是的,您需要消费者令牌和秘密来签署每个请求,问题是在哪里存储秘密。如果有人窃取了访问密钥,这没什么大不了的,因为它可以被撤销,但如果有人获得了消费者密钥,则您应用的每个副本都已被盗用。 OAuth 1 要求对每个请求进行签名。 OAuth 2 只需要访问令牌。获取令牌时都需要密钥和秘密。【参考方案4】:

一种解决方案可能是将 OAuth 密码硬编码到代码中,但 不是 为纯字符串。以某种方式混淆它 - 将其分割成段,将字符移动一个偏移量,旋转它 - 做任何或所有这些事情。破解者可以分析您的字节码并找到字符串,但混淆代码可能很难弄清楚。

这不是一个万无一失的解决方案,而是一个便宜的解决方案。

根据漏洞利用的价值,一些天才破解者可以不遗余力地找到您的密码。您需要权衡因素 - 前面提到的服务器端解决方案的成本、破解者花更多精力寻找您的密码的动机,以及您可以实施的混淆的复杂性。

【讨论】:

是的,我认为这是合理的。某人首先要提取消费者的秘密,然后抢夺人们的凭据来做一些卑鄙的事情,这需要很大的决心。对于备受瞩目的应用程序,我不确定这是否足够,但对于普通应用程序,我认为你是对的,你必须在实施时间与相当小的安全威胁之间取得平衡。 只需一位用户付出努力,然后发布或分享您的秘密。一旦您的秘密泄露,您的服务因滥用而被完全关闭的风险就会飙升,而且完全超出您的控制范围。 混淆根本不是安全性。这比没有安全还要糟糕,因为它给了开发人员一种错误的安全感。 en.wikipedia.org/wiki/Security_through_obscurity “混淆根本就不是安全。这比没有安全还要糟糕,因为它给了开发者一种虚假的安全感。”废话。没有人说混淆可以带来良好的安全性。但是,如果我要使用我的 apk 分发 OAuth 机密,那么混淆肯定比不混淆要好。混淆是谷歌在应用内存储密钥/秘密时也推荐的做法。如果不出意外,这些措施可以阻止普通黑客,这总比没有好。像您这样的笼统陈述将不完善的安全性等同于没有安全性。这根本不是真的。不完美就是不完美。 混淆没有帮助,因为无论你做了多少转换或编码,你仍然一起构建密钥并使用它来构建你的 API 请求。在正确的位置动态挂钩 API 以在 HTTPS 加密之前转储您发送的请求是相当简单的。所以请不要在你的应用程序中嵌入密钥,除非真的没有其他可能的选择。【参考方案5】:

这里有一些需要考虑的事情。 Google 提供了两种 OAuth 方法...用于网络应用程序,您在其中注册域并生成唯一密钥,以及为您使用“匿名”密钥的已安装应用程序。

也许我在阅读中掩盖了某些内容,但与已安装的应用程序共享您的 web 应用程序的唯一密钥似乎比在官方安装的应用程序方法中使用“匿名”更安全。

【讨论】:

【参考方案6】:

严格来说,Facebook 还没有实施 OAuth(目前),但他们已经实施了一种方法,让您不要在 iPhone 应用程序中嵌入您的秘密:https://web.archive.org/web/20091223092924/http://wiki.developers.facebook.com/index.php/Session_Proxy

至于 OAuth,是的,我想得越多,我们就有点吃不消了。也许this 会解决它。

【讨论】:

wiki.developers.facebook.com 已死。【参考方案7】:

我同意 Felixyz 的观点。 OAuth 虽然比 Basic Auth 更好,但要成为移动应用程序的良好解决方案还有很长的路要走。我一直在尝试使用 OAuth 向 Google App Engine 应用程序验证手机应用程序。您无法在移动设备上可靠地管理消费者机密这一事实意味着默认使用“匿名”访问。

Google App Engine OAuth 实施的浏览器授权步骤会将您带到一个页面,其中包含如下文本: “网站 正在请求访问您的 Google 帐户以获取下列产品”

YourApp(yourapp.appspot.com) - 不隶属于 Google

它从您提供的回调 url 中使用的域/主机名中获取 ,如果您使用自定义方案来拦截回调,则它可以是 Android 上的任何内容。 因此,如果您使用“匿名”访问或您的消费者机密被泄露,那么任何人都可以编写消费者来欺骗用户访问您的 gae 应用程序。

Google OAuth 授权页面还包含大量警告,根据您使用的是“匿名”、消费者机密还是公钥,这些警告分为 3 级严重性。

对于不懂技术的普通用户来说,这是相当可怕的事情。我不希望这种方式的注册完成率很高。

这篇博文阐明了消费者机密实际上如何不适用于已安装的应用程序。 http://hueniverse.com/2009/02/should-twitter-discontinue-their-basic-auth-api/

【讨论】:

【参考方案8】:

我还在尝试提出一种用于移动 OAuth 身份验证的解决方案,并将秘密存储在一般的应用程序包中。

我突然想到了一个疯狂的想法:最简单的想法是将秘密存储在二进制文件中,但以某种方式进行了混淆,或者换句话说,您存储了一个加密的秘密。所以,这意味着你必须存储一个密钥来解密你的秘密,这似乎让我们绕了一圈。但是,为什么不直接使用操作系统中已经存在的密钥,即它是由操作系统而不是您的应用程序定义的。

所以,为了澄清我的想法,你选择一个由操作系统定义的字符串,不管是哪个。然后使用此字符串作为密钥加密您的秘密,并将其存储在您的应用程序中。然后在运行时,使用密钥解密变量,这只是一个操作系统常量。任何偷看你的二进制文件的黑客都会看到一个加密的字符串,但没有密钥。

这行得通吗?

【讨论】:

好主意,但没有。破解者只会看到指向 OS 常量地址的二进制文件。【参考方案9】:

这里我已经回答了将您的 oAuth 信息存储在移动应用程序中的安全方法

https://***.com/a/17359809/998483

https://sites.google.com/site/greateindiaclub/mobil-apps/ios/securelystoringoauthkeysiniosapplication

【讨论】:

【参考方案10】:

使用 OAUth 2.0,您可以将机密存储在服务器上。使用服务器获取访问令牌,然后将其移动到应用程序,您可以直接从应用程序调用资源。

对于 OAuth 1.0 (Twitter),进行 API 调用需要密钥。通过服务器代理调用是确保机密不被泄露的唯一方法。

两者都需要一些机制,您的服务器组件知道它是您的客户端调用它。这往往在安装时完成,并使用特定于平台的机制在调用您的服务器时获取某种应用程序 ID。

(我是 OAuth 2.0 规范的编辑)

【讨论】:

您能否详细说明“获取某种应用程序 ID 的平台特定机制”?服务器组件如何验证客户端的身份?我认为这可以通过客户端配置来完成。例如,为每个客户端部署一个新的唯一 SSL 证书。你是这个意思吗?如果比这更复杂,也许你可以参考更深入的文章? 我记得一些安全人员谈论如何做到这一点。对操作系统的调用会返回一个签名令牌,然后您可以将其发送到您的服务器并进行验证。抱歉,我没有具体细节。这是一个错误,可以使用一些很好的例子。 @DickHardt 但在这种情况下,您如何确保移动应用程序确实是您的应用程序而不是欺诈应用程序?【参考方案11】:

使用 OAuth 2.0,您可以简单地使用客户端流程来获取访问令牌,然后使用此访问令牌来验证所有进一步的请求。那么你就完全不需要秘密了。

可以在此处找到有关如何实现此功能的详细说明:https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified#mobile-apps

【讨论】:

如果服务支持“客户端流程”。许多人不这样做,而是需要客户端 ID 和客户端密码才能获得此访问令牌。【参考方案12】:

不要将秘密存储在应用程序中。

您需要有一个可以被应用程序通过 https 访问的服务器(很明显),并且您将秘密存储在上面。

当有人想通过您的移动/桌面应用程序登录时,您的应用程序将简单地将请求转发到服务器,然后服务器将附加密钥并将其发送给服务提供商。然后,您的服务器可以告诉您的应用程序是否成功。

然后,如果您需要从服务(facebook、google、twitter 等)获取任何敏感信息,应用程序会询问您的服务器,只有在正确连接的情况下,您的服务器才会将其提供给应用程序。

除了将其存储在服务器上之外,实际上没有其他选择。客户端没有什么是安全的。

注意

也就是说,这只会保护您免受恶意客户端的攻击,但不能保护客户端免受恶意客户端的攻击,也不能保护客户端免受其他恶意客户端的攻击(网络钓鱼)...

OAuth 在浏览器中是比桌面/移动设备更好的协议。

【讨论】:

这不是让黑客的生活更轻松吗?!因为现在,为了访问服务器资源,我们在技术上只需要客户端 ID,因为服务器无论如何都会将秘密附加到请求中。我错过了什么吗? @HudiIlfeld 是的,您缺少一些东西:客户端需要登录到服务器。只要他没有登录,服务器就不会返回任何东西。管理此问题的一种方法是在第一次发送凭据后,服务器将访问令牌返回给客户端,然后客户端在以后的每个请求中发送此访问令牌。这里有很多选择。 @Gudradain 我不确定您的解决方案在这里有什么帮助,因为所有这些都可以自动化:1)客户端将 client_id 发送到服务器。 2) 服务器返回一个访问令牌供客户端在下一个请求中发送它?为什么?但是让我们假设它没问题。 3) 黑客现在已通过服务器身份验证,并且仍然能够发出他想要的任何 API/服务请求,仍然在您的代理服务器后面被冒充。我在这里错过了什么吗? @IvoPereira 将客户端密码放在应用程序中可以很容易地窃取它。一旦有人拥有您的客户 ID 和客户密码,他们就可以冒充客户。如果有人根据应用程序冒充客户,可能会造成不同程度的损害。如果您想了解更多信息,我建议您再问一个问题(不是评论)。 @Gudradain 出于您提到的原因,我并没有建议确切的流程,但是仅在中间使用代理并不能解决问题,因为它会打开另一个免费的大门坏演员。但是,这可能有助于缓解问题:medium.com/@benjamin.botto/…(增强架构 -> 安全注意事项)【参考方案13】:

授权代码授予类型有一个新的扩展,称为代码交换证明密钥 (PKCE)。有了它,您就不需要客户端密码了。

PKCE (RFC 7636) 是一种保护不使用公共客户端的技术 客户机密。

它主要用于本地和移动应用程序,但该技术可以 也适用于任何公共客户。它需要额外的 授权服务器支持,因此仅支持 某些提供商。

来自https://oauth.net/2/pkce/

欲了解更多信息,您可以阅读完整的RFC 7636 或this short introduction。

【讨论】:

请注意,这仍然可能导致客户端假冒:tools.ietf.org/html/rfc6749#section-10.2【参考方案14】:

这些解决方案都不能阻止坚定的黑客嗅探从他们的移动设备(或模拟器)发送的数据包,以查看 http 标头中的客户端密码。

一种解决方案可能是拥有一个动态机密,该机密由使用私有 2 路加密密钥和算法加密的时间戳组成。然后,该服务解密该密钥并确定时间戳是否为 +/- 5 分钟。

这样,即使机密被泄露,黑客也最多只能使用 5 分钟。

【讨论】:

以上是关于移动应用程序中的 OAuth 机密的主要内容,如果未能解决你的问题,请参考以下文章

在移动 Web 应用程序中存储“应用程序机密”的最佳方式是啥?

oAuth2 离子移动应用程序中的登录问题

WSO2 Api Manager公共本地移动应用中的OAuth2 DCR安全性

使用oauth 2.0中的指纹登录

什么是移动应用程序的正确 OAuth 2.0 流程

如果存在移动应用而不是 Web 表单,则 Twitter OAuth 应打开移动应用以进行授权