为移动应用程序创建 API - 身份验证和授权
Posted
技术标签:
【中文标题】为移动应用程序创建 API - 身份验证和授权【英文标题】:Creating an API for mobile applications - Authentication and Authorization 【发布时间】:2011-04-27 04:56:21 【问题描述】:概述
我希望为我的应用程序创建一个 (REST) API。最初/主要目的是供移动应用程序(iPhone、android、Symbian 等)使用。我一直在研究基于 Web 的 API 的不同身份验证和授权机制(通过研究其他实现)。我已经了解了大部分基本概念,但仍在寻找一些领域的指导。我想做的最后一件事是重新发明***,但我没有找到任何符合我标准的标准解决方案(但是我的标准被误导了,所以也可以随意批评)。此外,我希望 API 对于所有使用它的平台/应用程序都是相同的。
oAuth
我将继续提出我对 oAuth 的反对意见,因为我知道这可能是提供的第一个解决方案。对于移动应用程序(或更具体地说是非 Web 应用程序),离开应用程序(转到 Web 浏览器)进行身份验证似乎是错误的。此外,浏览器无法(我知道)将回调返回给应用程序(尤其是跨平台)。我知道有几个应用程序可以做到这一点,但它只是感觉不对劲,并且会影响应用程序的用户体验。
要求
-
用户在应用程序中输入用户名/密码。
每个 API 调用都由调用应用程序标识。
开销保持在最低限度,身份验证方面对开发人员来说很直观。
该机制对最终用户(他们的登录凭据未公开)和开发人员(他们的应用程序凭据未公开)都是安全的。
如果可能,不需要 https(绝不是硬性要求)。
我目前对实施的看法
外部开发人员将请求一个 API 帐户。他们将收到一个 apikey 和 apisecret。每个请求至少需要三个参数。
apikey - 在注册时提供给开发人员 时间戳 - 兼作给定 apikey 的每条消息的唯一标识符 hash - 时间戳 + apisecret 的哈希需要 apikey 来识别发出请求的应用程序。时间戳的作用类似于 oauth_nonce 并避免/减轻重放攻击。哈希确保请求实际上是从给定 apikey 的所有者发出的。
对于经过身份验证的请求(代表用户完成的请求),我仍然不确定是使用 access_token 路由还是使用用户名和密码哈希组合。无论哪种方式,在某些时候都需要用户名/密码组合。因此,当它这样做时,将使用几条信息(apikey、apisecret、时间戳)+密码的哈希值。 我希望得到有关这方面的反馈。仅供参考,他们必须先对密码进行哈希处理,因为我不会在没有哈希的情况下将密码存储在我的系统中。
结论
仅供参考,这不是关于如何构建/构造 API 的一般请求,而是关于如何仅在应用程序内处理身份验证和授权的请求。
随机想法/奖金问题
对于仅需要 apikey 作为请求一部分的 API,您如何防止 apikey 所有者以外的其他人看到 apikey(因为以明文形式发送)并发出过多请求以将其推送到使用限制?也许我只是在想这个,但不应该有一些东西来验证请求是否已向 apikey 所有者验证?就我而言,这就是 apisecret 的目的,它永远不会在未经过哈希处理的情况下显示/传输。
说到哈希,md5 和 hmac-sha1 呢?用足够长的数据(即 apisecret)对所有值进行哈希处理真的很重要吗?
我之前一直在考虑将每个用户/行的盐添加到我的用户密码哈希中。如果我这样做,应用程序如何在不知道所使用的盐的情况下创建匹配的哈希?
【参考方案1】:派对迟到了,但我想为任何对此问题感兴趣的人提供一些额外的考虑。我在一家从事移动 API 安全解决方案 (approov) 的公司工作,所以这整个领域绝对与我的兴趣相关。
首先,在尝试保护移动 API 时要考虑的最重要的事情是它对您有多大价值。适合银行的解决方案不同于适合只是为了好玩的人的解决方案。
在建议的解决方案中,您提到至少需要三个参数:
apikey - 在注册时提供给开发者 时间戳 - 兼作给定 apikey 的每条消息的唯一标识符 hash - 时间戳 + apisecret 的哈希这意味着对于某些 API 调用,不需要用户名/密码。这对于您不想强制登录的应用程序(例如在网上商店中浏览)很有用。
这与用户身份验证问题略有不同,更像是软件的身份验证或证明。没有用户,但您仍要确保没有恶意访问您的 API。因此,您使用您的 API 密钥对流量进行签名,并将访问 API 的代码识别为真实代码。此解决方案的潜在问题是您必须在应用程序的每个版本中泄露秘密。如果有人可以提取秘密,他们就可以使用您的 API,冒充您的软件,但可以为所欲为。
为了应对这种威胁,您可以根据数据的价值采取多种措施。 混淆是一种简单的方法,使提取秘密变得更加困难。有一些工具可以为您做到这一点,对于 Android 来说更是如此,但您仍然必须拥有生成哈希的代码,并且足够熟练的人总是可以直接调用执行哈希的函数。
避免过度使用不需要登录的 API 的另一种方法是限制流量并可能识别和阻止可疑 IP 地址。您想要付出的努力在很大程度上取决于您的数据的价值。
除此之外,您还可以轻松地开始涉足我日常工作的领域。无论如何,这是保护 API 安全的另一个方面,我认为这很重要,并希望指出。
【讨论】:
【参考方案2】:我正在考虑在我的项目中进行登录部分的方式是:
在登录之前,用户从服务器请求login_token
。这些是根据请求生成并存储在服务器上的,并且可能具有有限的生命周期。
登录应用程序计算用户密码的哈希值,然后将密码与login_token
进行哈希处理得到一个值,然后他们返回login_token
和组合哈希值。
服务器检查login_token
是它生成的,将其从有效login_token
s 列表中删除。然后,服务器将其存储的用户密码哈希与login_token
组合起来,并确保它与提交的组合令牌相匹配。如果匹配,您已经验证了您的用户。
这样做的好处是您永远不会将用户的密码存储在服务器上,密码永远不会以明文形式传递,密码哈希仅在帐户创建时以明文形式传递(尽管可能有解决方法),并且由于login_token
在使用时已从数据库中删除,因此它应该不会受到重放攻击。
【讨论】:
谢谢,我忘了在应用程序端添加关于散列密码的部分。我不以明文形式存储我的用户密码(我在存储之前进行哈希处理)。 我看到了这种方法的一个缺点:您不能在数据库中存储加盐密码。如果攻击者将手放在您的数据库上,他们将不需要进行任何解密。由于密码哈希是该方案中的真实密码。 @sigod 你是对的。虽然我认为有一个基本的二分法——你要么需要信任你的传输,要么你需要信任你的存储。使用加盐密码的登录系统信任传输层 - 因此将密码从用户以明文形式传递给身份验证系统。这种情况下不信任传输层(我认为这是因为我的目标平台对 SHTTP 的支持不好)。如果您信任传输层,您或许可以做出其他取舍。 我有 3 个问题: 1) 用户的密码怎么从来没有存储在服务器上?在第 2 步中,您提到它存储散列用户的密码。 2) 用户密码永远不会以明文形式传递。但实际上是在客户端进行哈希处理,然后与服务器上的哈希密码进行比较,这与明文传递和存储几乎相同,这样做有什么意义呢? 3)这种方法假设攻击者不知道login_token+密码是如何被hash的,这违反了Kerckhoffs原则,确实不安全。【参考方案3】:Twitter 通过支持他们称为 xAuth 的变体解决了 oAuth 中的外部应用程序问题。不幸的是,已经有大量其他方案使用此名称,因此整理起来可能会令人困惑。
协议是 oAuth,除了它跳过请求令牌阶段并在收到用户名和密码后立即发出访问令牌对。 (从step E here 开始。)初始 请求和响应必须受到保护 - 它以明文形式发送用户名和密码,并接收回访问令牌和秘密令牌。配置访问令牌对后,初始令牌交换是通过 oAuth 模型还是 xAuth 模型与会话其余部分的客户端和服务器无关。这样做的好处是您可以利用现有的 oAuth 基础架构,并且对移动/Web/桌面应用程序具有几乎相同的实现。主要缺点是应用程序被授予访问客户端用户名和密码的权限,但您的要求似乎要求采用这种方法。
无论如何,我想同意您的直觉以及这里其他几位回答者的直觉:不要尝试从头开始构建新的东西。安全协议可以很容易开始,但总是很难做好,而且它们越复杂,第三方开发人员就越不可能针对它们实施。您的假设协议与 o(x)Auth 非常相似 - api_key/api_secret、nonce、sha1 散列 - 但您的开发人员将需要自行推出众多现有库,而不是能够使用其中之一。
【讨论】:
我还应该指出,“跳过请求令牌”端点似乎将在 oAuth 2 中,它在当前草案中列为“密码”访问授权类型。请参阅第 4.1.2 节:tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.2 就像我对 Lonra 提到的那样,我正在更多地研究 xAuth,特别是出于您最后提到的原因...开发人员可以“现成”oAuth 工具/库来与我的 API 进行交互,这是“一件好事”。【参考方案4】:这是一大堆问题,我想很多人都没有读到最后:)
我对 Web 服务身份验证的经验是,人们通常会对其进行过度设计,并且问题与您在网页上遇到的问题相同。可能的非常简单的选项包括登录步骤的 https、返回令牌、要求将其包含在未来的请求中。您还可以使用 http 基本身份验证,只需在标头中传递内容。为了增加安全性,经常轮换/过期令牌,检查请求是否来自同一个 IP 块(这可能会因为移动用户在单元之间移动而变得混乱),结合 API 密钥或类似的。或者,在对用户进行身份验证之前执行 oauth 的“请求密钥”步骤(已经有人在之前的答案中提出了这个建议,这是一个好主意),并将其用作生成访问令牌所需的密钥。
xAuth 是我尚未使用但我听说过很多关于 oAuth 的设备友好型替代方案的替代方案。看看它,如果你使用它,那么我真的很想听听你的印象。
对于散列,sha1 稍微好一点,但不要对此束手无策 - 无论设备可以轻松(并且在性能方面快速)实现都可能没问题。
希望对你有帮助,祝你好运:)
【讨论】:
感谢您的回复。我一直在研究 xAuth,这可能是我要走的路,这样我就可以安装 oAuth,这使得与 API 交互的过程更加标准化。【参考方案5】:那么您所追求的是某种服务器端身份验证机制来处理移动应用程序的身份验证和授权方面?
假设是这种情况,那么我将按以下方式处理它(但只是'因为我是 Java 开发人员,所以 C# 人会做不同的事情):
RESTful 身份验证和授权服务
-
这只能通过 HTTPS 工作以防止窃听。
它将基于RESTEasy、Spring Security 和CAS 的组合(用于跨多个应用程序的单点登录)。
它适用于浏览器和支持 Web 的客户端应用程序
将有一个基于 Web 的帐户管理界面,允许用户编辑其详细信息,并允许管理员(针对特定应用程序)更改授权级别
客户端安全库/应用程序
-
对于每个支持的平台(例如
Symbian、Android、ios 等)创建一个
适当的实施
本机中的安全库
平台语言(例如 Java、
ObjectiveC、C 等)
图书馆
应该管理 HTTPS 请求
使用可用的 API 形成
对于给定的平台(例如 Java
使用 URLConnection 等)
一般认证的消费者和
授权库('因为那是
全部)将编码到特定的
界面,如果它不会很高兴
永远改变所以确保它非常
灵活的。遵循现有设计
选择,例如 Spring Security。
既然 30,000 英尺的视野已经完成,您将如何做呢?好吧,使用浏览器客户端在服务器端创建基于所列技术的身份验证和授权系统并不难。与 HTTPS 相结合,框架将提供一个基于由身份验证过程生成的共享令牌(通常显示为 cookie)的安全过程,并在用户希望做某事时使用。每当发生任何请求时,客户端都会将此令牌提供给服务器。
对于本地移动应用程序,您似乎正在寻求执行以下操作的解决方案:
-
客户端应用程序具有定义的访问控制列表 (ACL),用于控制运行时对方法调用的访问。例如,给定用户可以从方法中读取集合,但他们的 ACL 只允许访问名称中带有 Q 的对象,因此集合中的某些数据会被安全拦截器安静地提取。在 Java 中,这很简单,您只需在调用代码上使用 Spring Security 注释并实现合适的 ACL 响应过程。在其他语言中,您只能靠自己,并且可能需要提供调用安全库的样板安全代码。如果该语言支持 AOP(面向方面的编程),那么在这种情况下充分利用它。
安全库将完整的授权列表缓存到当前应用程序的私有内存中,这样它就不必保持连接状态。根据登录会话的长度,这可能是一次性操作,永远不会重复。
无论你做什么,不要试图发明你自己的安全协议,或者默默无闻地使用安全性。您将永远无法为此编写比当前可用且免费的算法更好的算法。此外,人们信任众所周知的算法。因此,如果您说您的安全库使用 SSL、HTTPS、SpringSecurity 和 AES 加密令牌的组合为本地移动应用程序提供授权和身份验证,那么您将立即在市场上获得信誉。
希望这对您有所帮助,并祝您事业顺利。如果您想了解更多信息,请告诉我——我已经编写了很多基于 Spring Security、ACL 等的 Web 应用程序。
【讨论】:
谢谢,好信息。几个问题。首先,如果窃听是可以接受的(不确定是否是/不是,我的应用程序没有任何真正的个人/有价值的信息,但如果它确实我的理由会改变)那么实际上需要 HTTPS 吗? 如果您愿意,您可以在 HTTPS 之外操作整个系统。 HTTPS 只是为了保护秘密信息,所以我假设在身份验证阶段你会通过 HTTPS 来提供一些保证你的用户名/密码/秘密是保密的。在响应中移交令牌后,如果流中包含的信息(需要进行身份验证才能获得)不需要防止窃听者,则可以明文发出进一步的请求。 另外,对 CAS 身份验证协议的描述可能有用:jasig.org/cas/protocol以上是关于为移动应用程序创建 API - 身份验证和授权的主要内容,如果未能解决你的问题,请参考以下文章
Azure:为 API 应用程序配置身份验证/授权。快速配置,找不到应用程序
为网站和移动应用程序混合 MVC 5 + WEB API 2 身份验证
如何从移动应用和节点 API 处理 Firebase 身份验证
如何在 Laravel 中为 REST API 创建身份验证