基于 REST API 令牌的身份验证

我正在开发一个需要身份验证的 REST API。由于身份验证本身是通过 HTTP 上的外部 Web 服务进行的,因此我推断我们将分发令牌以避免重复调用身份验证服务。这就引出了我的第一个问题:

这真的比仅仅要求客户机在每个请求上使用 HTTPBasicAuth 并缓存对身份验证服务服务器端的调用更好吗?

BasicAuth 解决方案的优点在于,在开始内容请求之前,不需要完全往返于服务器。令牌可能在范围上更加灵活(即只授予特定资源或操作的权限) ,但是这似乎比我的简单用例更适合于 OAuth 上下文。

目前的令牌是这样获得的:

curl -X POST localhost/token --data "api_key=81169d80...
&verifier=2f5ae51a...
&timestamp=1234567
&user=foo
&pass=bar"

所有请求都需要 api_keytimestampverifier。“验证器”由以下人员返回:

sha1(timestamp + api_key + shared_secret)

My intention is to only allow calls from known parties, and to prevent calls from being reused verbatim.

这样够了吗? 过度杀戮? 过度杀戮?

有了令牌,客户端就可以获取资源:

curl localhost/posts?api_key=81169d80...
&verifier=81169d80...
&token=9fUyas64...
&timestamp=1234567

For the simplest call possible, this seems kind of horribly verbose. Considering the shared_secret will wind up being embedded in (at minimum) an iOS application, from which I would assume it can be extracted, is this even offering anything beyond a false sense of security?

180642 次浏览

让我把所有事情分开来,孤立地解决每一个问题:

认证

对于身份验证,baseauth 的优势在于它是协议级别的成熟解决方案。这意味着许多 “可能会突然出现”问题已经为您解决了。例如,对于 BaseAuth,用户代理知道密码是一个密码,因此不会缓存它。

授权服务器负载

如果您向用户分发令牌而不是在服务器上缓存身份验证,那么您仍然在做同样的事情: 缓存身份验证信息。唯一的区别是您将缓存的责任转交给用户。对于没有收获的用户来说,这似乎是不必要的劳动,因此我建议按照您的建议在服务器上透明地处理这个问题。

传输安全

如果可以使用 SSL 连接,那么就是这样,连接是安全的 * 。为了防止意外的多次执行,您可以过滤多个 URL 或要求用户在 URL 中包含一个随机组件(“ nonce”)。

url = username:key@myhost.com/api/call/nonce

如果这是不可能的,并且传输的信息不是秘密的,我建议使用散列来保护请求,正如您在令牌方法中建议的那样。由于哈希提供了安全性,因此可以指示用户提供哈希作为 baseauth 密码。为了提高健壮性,我建议使用随机字符串而不是时间戳作为“ nonce”来防止重播攻击(可以在同一秒内发出两个合法请求)。与提供单独的“共享秘密”和“ api 密钥”字段不同,您可以简单地使用 api 密钥作为共享秘密,然后使用不变的 salt 来防止彩虹表攻击。用户名字段似乎也是放置 nonce 的好地方,因为它是 auth 的一部分。所以现在你有了这样一个明确的决定:

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:one_time_key@myhost.com/api/call

这确实有点费力。这是因为您没有使用协议级别的解决方案(如 SSL)。因此,向用户提供某种 SDK 可能是一个好主意,这样至少他们不必亲自阅读。如果您需要这样做,我认为安全级别是合适的(just-right-kill)。

Secure secret storage

这取决于你想阻止谁。如果你阻止访问用户手机的人以用户的名义使用 REST 服务,那么最好在目标操作系统上找到某种 keyring API,让 SDK (或实现者)在那里存储密钥。如果这不可能,那么至少可以通过加密、将加密数据和加密密钥分别存储在不同的位置来增加获取秘密的难度。

如果您试图阻止其他软件供应商获得您的 API 密钥,以防止开发替代客户机,那么只有加密和分别存储的方法 差不多可以工作。这是白盒加密,到目前为止,还没有人想出一个真正安全的解决方案来解决这个类的问题。您至少可以为每个用户发出一个密钥,这样您就可以禁止滥用密钥。

(*)编辑: < em > 没有 采取额外步骤核实的 SSL 连接 should no longer be considered secure

一个纯 RESTful API 应该使用底层协议标准特性:

  1. 对于 HTTP,RESTful API 应该符合现有的 HTTP 标准头。添加新的 HTTP 头违反了 REST 原则。不要重新发明轮子,使用 HTTP/1.1标准中的所有标准特性——包括状态响应代码、头文件等等。RESTFul Web 服务应该利用并依赖 HTTP 标准。

  2. RESTful services MUST be STATELESS. Any tricks, such as token based authentication that attempts to remember the state of previous REST requests on the server violates the REST principles. Again, this is a MUST; that is, if you web server saves any request/response context related information on the server in attempt to establish any sort of session on the server, then your web service is NOT Stateless. And if it is NOT stateless it is NOT RESTFul.

底线: 出于身份验证/授权的目的,您应该使用 HTTP 标准授权头。也就是说,您应该在每个需要进行身份验证的后续请求中添加 HTTP 授权/身份验证头。RESTAPI 应该遵循 HTTP 身份验证方案标准。这个头应该如何格式化的细节在 rfc2616 HTTP 1.1标准-第14.8节 rfc2616授权和 rfc2617 HTTP 认证: 基本和 HTTP摘要认证中定义。

我已经开发了一个 RESTful 服务的思科首相性能管理器应用程序。在 Google 中搜索我为该应用程序编写的 REST API 文档,以获得有关 RESTFulAPI 遵从性 给你的更多详细信息。在该实现中,我选择使用 HTTP“基本”授权方案。- 查阅该 REST API 文档的1.5或以上版本,并在该文档中搜索授权。

在 Web 中,有状态协议基于一个临时令牌,该令牌在浏览器和服务器之间(通过 cookie 头或 URI 重写)对每个请求进行交换。这个令牌通常是在服务器端创建的,它是一段具有特定生存时间的 不透明数据,它的唯一目的是标识特定的 Web 用户代理。也就是说,令牌是临时的,并且成为一个状态,Web 服务器必须在会话期间代表客户端用户代理维护该状态。因此,以这种方式使用令牌的通信是 STATEFUL。如果客户机和服务器之间的对话是 STATEFUL,那么它就不是 RESTful。

用户名/密码(在 Authorization 头上发送)通常保存在数据库中,目的是标识用户。有时用户可能指的是另一个应用程序; 但是,用户名/密码是 从来没有,用于识别特定的 Web 客户机用户代理。Web 代理和服务器之间的对话基于使用用户名/密码的授权头(在 HTTP 基本授权之后)是 STATELSS,因为 Web 服务器前端没有代表特定的 Web 客户端用户代理创建或维护任何状态 资料。基于我对 REST 的理解,协议明确规定客户端和服务器之间的对话应该是 STATELSS。因此,如果我们想要一个真正的 RESTful 服务,我们应该使用用户名/密码(参考我在前面的文章中提到的 RFC)在授权头中为每一个调用,而不是一种感知类型的令牌(例如,在 Web 服务器中创建的会话令牌,在授权服务器中创建的 OAuth 令牌,等等)。

据我所知,一些被称为 REST 提供者正在使用类似 OAuth1或 OAuth2接受令牌的令牌,以便在 HTTP 头中作为“ Authorization: Bearer”传递。然而,在我看来,将这些令牌用于 RESTful 服务将违反 REST 所拥有的真正的 STATELSS 意义; 因为这些令牌是服务器端创建/维护的 暂时的数据片段,用于识别 Web 客户端/服务器对话的有效持续时间的特定 Web 客户端用户代理。因此,任何使用这些 OAuth1/2令牌的服务都不应该被称为 REST,如果我们想要坚持无状态协议的真正含义的话。

鲁本斯