192

Overview

I'm looking to create a (REST) API for my application. The initial/primary purpose will be for consumption by mobile apps (iPhone, Android, Symbian, etc). I've been looking into different mechanisms for authentication and authorization for web-based APIs (by studying other implementations). I've got my head wrapped around most of the fundamental concepts but am still looking for guidance in a few areas. The last thing I want to do is reinvent the wheel, but I'm not finding any standard solutions that fits my criteria (however my criteria my be misguided so feel free to critique that as well). Additionally, I want the API to be the same for all platforms/applications consuming it.

oAuth

I'll go ahead and throw out my objection to oAuth since I know that will likely be the first solution offered. For mobile applications (or more specifically non-web applications), it just seems wrong to leave the application (to go to a web-browser) for the authentication. Additionally, there is no way (I am aware of) for the browser to return the callback to the application (especially cross-platform). I know a couple of apps that do that, but it just feels wrong and gives a break in the application UX.

Requirements

  1. User enters username/password into application.
  2. Every API call is identified by the calling application.
  3. Overhead is kept to a minimum and the auth aspect is intuitive for developers.
  4. The mechanism is secure for both the end user (their login credentials are not exposed) as well as the developer (their application credentials are not exposed).
  5. If possible, not require https (by no means a hard requirement).

My Current Thoughts on Implementation

An external developer will request an API account. They will receive an apikey and apisecret. Every request will require at minimum three parameters.

  • apikey - given to developer at regisration
  • timestamp - doubles as a unique identifier for each message for a given apikey
  • hash - a hash of the timestamp + the apisecret

The apikey is required to identify the application issuing the request. The timestamp acts similarly to the oauth_nonce and avoids/mitigates replay attacks. The hash ensures that request was actually issued from the owner of the given apikey.

For authenticated requests (ones done on the behalf of a user), I'm still undecided between going with an access_token route or a username and password hash combo. Either way, at some point a username/password combo will be required. So when it does, a hash of several pieces of information (apikey, apisecret, timestamp) + the password would be used. I'd love feedback on this aspect. FYI, they would have to hash the password first, since I don't store the passwords in my system without hashing.

Conclusion

FYI, this isn't a request for how to build/structure the API in general only how to handle the authentication and authorization from solely within an application.

Random Thoughts/Bonus Questions

For APIs that only require an apikey as part of the request, how do you prevent someone other than the apikey owner from being able to see the apikey (since sent in the clear) and make excessive requests to push them over usage limits? Maybe I'm just over thinking this, but shouldn't there be something to authenticate that a request was verified to the apikey owner? In my case, that was the purpose of the apisecret, it is never shown/transmitted without being hashed.

Speaking of hashes, what about md5 vs hmac-sha1? Does it really matter when all of the values are hashed with with sufficiently long data (ie. apisecret)?

I had been previously considering adding a per user/row salt to my users password hash. If I were to do that, how could the application be able to create a matching hash without knowing the salt used?

4

5 回答 5

45

我正在考虑在我的项目中执行此登录部分的方式是:

  1. 在登录之前,用户login_token从服务器请求一个。这些是根据请求生成并存储在服务器上的,并且可能具有有限的生命周期。

  2. 登录应用程序计算用户密码的哈希值,然后用 对密码进行哈希处理login_token以获得一个值,然后他们返回login_token和组合的哈希值。

  3. 服务器检查login_token它是否已生成,将其从有效login_tokens 列表中删除。然后,服务器将其存储的用户密码哈希与login_token并确保它与提交的组合令牌相匹配。如果匹配,您已经验证了您的用户。

这样做的好处是您永远不会将用户的密码存储在服务器上,密码永远不会以明文形式传递,密码哈希仅在创建帐户时以明文形式传递(尽管可能有解决方法),并且应该是由于login_token在使用时从数据库中删除,因此免受重放攻击。

于 2010-10-19T05:59:48.823 回答
14

这是一大堆问题,我想很多人都没有读到最后:)

我对 Web 服务身份验证的经验是,人们通常会对其进行过度设计,并且问题与您在网页上遇到的问题相同。可能的非常简单的选项包括登录步骤的 https、返回令牌、要求将其包含在未来的请求中。您还可以使用 http 基本身份验证,只需在标头中传递内容。为了增加安全性,经常轮换/过期令牌,检查请求是否来自同一个 IP 块(这可能会因为移动用户在单元之间移动而变得混乱),结合 API 密钥或类似的。或者,在对用户进行身份验证之前执行 oauth 的“请求密钥”步骤(已经有人在之前的答案中提出了这个建议,这是一个好主意),并将其用作生成访问令牌的必需密钥。

xAuth 是我还没有使用过的替代方法,但我听说过很多关于 oAuth 的设备友好型替代方法。看看它,如果你使用它,那么我真的很想听听你的印象。

对于散列,sha1 稍微好一点,但不要挂断它 - 无论设备可以轻松(并且在性能意义上快速)实现都可能没问题。

希望有帮助,祝你好运:)

于 2010-10-29T08:20:35.863 回答
9

那么您所追求的是某种服务器端身份验证机制,它将处理移动应用程序的身份验证和授权方面?

假设是这种情况,那么我将按如下方式处理它(但只是因为我是一名 Java 开发人员,所以 C# 的人会以不同的方式做这件事):

RESTful 身份验证和授权服务

  1. 这仅适用于 HTTPS 以防止窃听。
  2. 它将基于RESTEasySpring SecurityCAS(用于跨多个应用程序的单点登录)的组合。
  3. 它将与浏览器和支持 Web 的客户端应用程序一起使用
  4. 将有一个基于 Web 的帐户管理界面,允许用户编辑他们的详细信息,并且管理员(针对特定应用程序)可以更改授权级别

客户端安全库/应用程序

  1. 对于每个支持的平台(例如 Symbian、Android、iOS 等),以平台的本地语言(例如 Java、ObjectiveC、C 等)创建安全库的合适实现
  2. 该库应使用给定平台的可用 API 管理 HTTPS 请求形成(例如,Java 使用 URLConnection 等)
  3. 通用身份验证和授权库(仅此而已)的使用者将编码到特定接口,并且如果它发生更改将不会高兴,因此请确保它非常灵活。遵循现有的设计选择,例如 Spring Security。

那么既然 30,000 英尺的视野已经完成,那么您将如何去做呢?好吧,使用浏览器客户端在服务器端创建基于所列技术的身份验证和授权系统并不难。结合 HTTPS,框架将提供一个基于由身份验证过程生成的共享令牌(通常显示为 cookie)的安全过程,并在用户希望做某事时使用。每当发生任何请求时,客户端都会将此令牌提供给服务器。

对于本地移动应用程序,您似乎正在寻求执行以下操作的解决方案:

  1. 客户端应用程序具有定义的访问控制列表 (ACL),用于控制对方法调用的运行时访问。例如,给定用户可以从方法中读取集合,但他们的 ACL 只允许访问名称中带有 Q 的对象,因此集合中的某些数据会被安全拦截器安静地提取。在 Java 中这很简单,您只需在调用代码上使用 Spring Security 注释并实现合适的 ACL 响应过程。在其他语言中,您只能靠自己,并且可能需要提供调用安全库的样板安全代码。如果该语言支持 AOP(面向方面​​编程),那么在这种情况下充分利用它。
  2. 安全库将完整的授权列表缓存到当前应用程序的私有内存中,这样它就不必保持连接状态。根据登录会话的长度,这可能是一个永远不会重复的一次性操作。

无论你做什么,都不要试图发明你自己的安全协议,或者默默无闻地使用安全性。您将永远无法为此编写比当前可用且免费的算法更好的算法。此外,人们信任众所周知的算法。因此,如果您说您的安全库使用 SSL、HTTPS、SpringSecurity 和 AES 加密令牌的组合为本地移动应用程序提供授权和身份验证,那么您将立即在市场上获得信誉。

希望这会有所帮助,并祝您事业顺利。如果您想了解更多信息,请告诉我——我已经编写了很多基于 Spring Security、ACL 等的 Web 应用程序。

于 2010-10-28T12:36:49.160 回答
9

Twitter 通过支持他们称为xAuth的变体解决了 oAuth 中的外部应用程序问题。不幸的是,已经有大量其他方案使用这个名称,因此整理起来可能会令人困惑。

该协议oAuth,除了它跳过请求令牌阶段并在收到用户名和密码后立即发出访问令牌对。(从此处的步骤 E 开始。)必须保护此初始请求和响应- 它以明文形式发送用户名和密码并接收访问令牌和秘密令牌。配置访问令牌对后,初始令牌交换是通过 oAuth 模型还是 xAuth 模型与会话其余部分的客户端和服务器无关。这样做的好处是您可以利用现有的 oAuth 基础架构,并且对移动/Web/桌面应用程序具有几乎相同的实现。主要缺点是应用程序被授予访问客户端用户名和密码的权限,但您的需求似乎要求使用这种方法。

无论如何,我想同意你的直觉以及这里其他几位回答者的直觉:不要试图从头开始构建新的东西。安全协议可以很容易开始,但总是很难做好,而且它们越复杂,第三方开发人员就越不可能针对它们实施。您的假设协议与 o(x)Auth - api_key/api_secret、nonce、sha1 散列非常相似 - 但您的开发人员将需要自行推出许多现有库,而不是能够使用其中之一。

于 2010-10-29T08:54:06.683 回答
6

派对迟到了,但我想为对这个问题感兴趣的人补充一些额外的观点。我在一家从事移动 API 安全解决方案 ( approov ) 的公司工作,所以这整个领域绝对与我的兴趣相关。

首先,在尝试保护移动 API 时要考虑的最重要的事情是它对您来说价值多少。银行的正确解决方案不同于只是为了好玩而做事的人的正确解决方案。

在建议的解决方案中,您提到至少需要三个参数:

  • apikey - 在注册时提供给开发人员
  • 时间戳 - 兼作给定 apikey 的每条消息的唯一标识符
  • hash - 时间戳 + apisecret 的哈希值

这意味着对于某些 API 调用,不需要用户名/密码。这对于您不想强制登录的应用程序(例如在网上商店中浏览)很有用。

这与用户身份验证问题略有不同,更像是软件的身份验证或证明。没有用户,但您仍要确保没有恶意访问您的 API。因此,您使用您的 API 密钥对流量进行签名,并将访问 API 的代码识别为真实的。此解决方案的潜在问题是您必须在应用程序的每个版本中泄露秘密。如果有人可以提取秘密,他们就可以使用您的 API,冒充您的软件,但可以为所欲为。

为了应对这种威胁,您可以根据数据的价值做很多事情。混淆是一种使提取秘密变得更加困难的简单方法。有一些工具可以为您做到这一点,对于 Android 来说更是如此,但您仍然必须拥有生成散列的代码,并且足够熟练的人总是可以直接调用执行散列的函数。

另一种缓解过度使用不需要登录的 API 的方法是限制流量并可能识别和阻止可疑 IP 地址。您想要付出的努力在很大程度上取决于您的数据的价值。

除此之外,您可以轻松地开始进入我日常工作的领域。无论如何,这是保护 API 的另一个方面,我认为它很重要并且想要标记出来。

于 2017-01-24T20:08:39.033 回答