REST API基于令牌的身份验证


122

我正在开发需要身份验证的REST API。因为身份验证本身是通过HTTP上的外部Web服务进行的,所以我认为我们将分配令牌以避免重复调用身份验证服务。巧妙地使我想到了第一个问题:

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

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

目前,令牌的获取方式如下:

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

api_keytimestamp并且verifier被所有请求所需。“验证程序”由以下方式返回:

sha1(timestamp + api_key + shared_secret)

我的意图是仅允许来自已知方的呼叫,并防止逐字重复使用呼叫。

这样够好吗?杀人 过度杀伤力?

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

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

对于最简单的呼叫,这似乎太冗长了。考虑到shared_secret将遗留在(至少)iOS应用程序中的意愿(我认为可以从中提取它),这是否提供了错误的安全感之外的任何东西?


2
而不是使用SHA1(时间戳+ API_KEY + shard_secret),你应该为一个更好的安全散列使用HMAC(shared_secret,timpestamp + API_KEY)en.wikipedia.org/wiki/Hash-based_message_authentication_code
米格尔A.卡拉斯科

@ MiguelA.Carrasco而且在2017年似乎已经达成共识,即bCrypt是新的哈希工具。
肖恩

Answers:


94

让我分开一切并单独解决每个问题:

认证方式

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

验证服务器负载

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

传输安全

如果可以使用SSL连接,仅此而已,那么该连接是安全的*。为防止意外的多次执行,您可以过滤多个URL或要求用户在URL中包括随机成分(“ nonce”)。

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

如果这是不可能的,并且传输的信息不是秘密的,那么我建议使用令牌方法中建议的散列来保护请求。由于哈希提供了安全性,因此您可以指示用户将哈希提供为baseauth密码。为了提高鲁棒性,我建议使用随机字符串而不是时间戳作为“即时”,以防止重放攻击(可以在同一秒内发出两个合法请求)。无需提供单独的“共享密钥”和“ api密钥”字段,您可以简单地将api密钥用作共享密钥,然后使用不变的盐防止彩虹表攻击。用户名字段似乎也是放置随机数的好地方,因为它是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可能是个好主意,这样至少他们不必自己经历它。如果您需要以这种方式进行操作,我会找到适当的安全级别(完全正确)。

安全的秘密存储

这取决于您要阻止的人。如果要阻止有权访问用户电话的用户使用用户名使用REST服务,那么最好在目标OS上找到某种密钥环API,并让SDK(或实现者)存储该密钥。关键在那里。如果无法做到这一点,则至少可以通过加密并在单独的位置存储加密的数据和加密密钥来使获取密码更加困难。

如果您试图阻止其他软件供应商获得您的API密钥以防止开发替代客户端,则只有加密和存储分开的方法几乎可行。这是白盒加密货币,迄今为止,还没有人提出过针对此类问题的真正安全的解决方案。您至少可以做的还是为每个用户发布一个密钥,这样就可以禁止滥用密钥。

(*)编辑: 如果采取其他步骤来验证 SSL连接,则不应再将其视为安全的 SSL连接。


感谢cmc,所有要点和深思熟虑。我最终采用了一种令牌/ HMAC方法,类似于您在上文中讨论的方法,而是采用了S3 REST API身份验证机制。
cantlin 2012年

如果您将令牌缓存在服务器上,那么它与旧的会话ID基本上不是一样的吗?会话ID是短暂的,并且还附加到快速缓存存储(如果实现了),以避免在每次请求时都命中数据库。真正的RESTful和无状态设计不应包含会话,但是如果您使用令牌作为ID并仍然访问数据库,那么使用会话ID会更好吗?或者,您可以使用JSON Web令牌,该令牌包含用于整个会话数据的加密或签名信息,以实现真正的无状态设计。
JustAMartin

16

纯RESTful API应该使用基础协议标准功能:

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

  2. RESTful服务必须是无状态的。任何技巧,例如试图记住服务器上先前REST请求状态的基于令牌的身份验证,都违反了REST原则。同样,这是必须的;也就是说,如果您的Web服务器将任何与请求/响应上下文相关的信息保存在服务器上,以尝试在服务器上建立任何类型的会话,则您的Web服务不是非无状态的。如果不是无状态的,则不是RESTFul。

底线:出于身份验证/授权的目的,您应该使用HTTP标准授权标头。也就是说,您应该在每个后续需要认证的请求中添加HTTP授权/认证标头。REST API应该遵循HTTP身份验证方案标准。在RFC 2616 HTTP 1.1标准– RFC 2616的14.8授权和RFC 2617 HTTP身份验证:基本和摘要访问身份验证中定义了如何格式化此标头。 。

我已经为Cisco Prime Performance Manager应用程序开发了RESTful服务。谷歌搜索的REST API文档,我写了该应用程序有关REST风格的API合规性的详细信息在这里。在该实现中,我选择使用HTTP“基本”授权方案。-检出该REST API文档的1.5版或更高版本,并在该文档中搜索授权。


8
“添加新的HTTP标头违反了REST原则”怎么办?而且,如果您愿意的话,您可能会很乐于解释在一段时间后过期的密码和在一段时间后过期的令牌之间的区别(关于原则)。
更好的Oliver,2015年

6
用户名+密码是令牌(!),可在每次请求时在客户端和服务器之间交换。该令牌在服务器上维护,并且有生存时间。如果密码过期,我必须获取一个新密码。您似乎将“令牌”与“服务器会话”相关联,但这是无效的结论。甚至无关紧要,因为这将是一个实现细节。您将用户名/密码以外的令牌分类为有状态的,恕我直言。
更好的Oliver,2015年

1
我认为您应该激发为什么要使用RESTful over Basic Authentication来实现,这是原始问题的一部分。也许您还可以链接到包含代码的良好示例。作为该学科的初学者,该理论似乎很清楚,有很多好的资源,但是实现方法却不多,而且实例很复杂。我感到很沮丧的是,似乎需要使用自定义编码来及时实施已经完成了数千次的事情。
JPK

13
-1“任何技巧,例如基于令牌的身份验证试图记住服务器上先前REST请求的状态,都违反了REST原则。” 基于令牌的身份验证与以前的REST请求的状态无关,并且不违反REST的无状态性
凯雷姆·贝多安(KeremBaydoğan)

1
因此,据此,JSON Web令牌违反了REST,因为它们可以存储用户的状态(声明)?无论如何,我更喜欢违反REST,并使用良好的旧会话ID作为“令牌”,但是初始身份验证是通过用户名+密码执行的,使用共享密钥和非常短的时间戳进行签名或加密(因此,如果有人尝试重播,它将失败。那)。在“企业级”应用程序中,很难舍弃会话的好处(避免在数据库中获取几乎每个请求所需的某些数据),因此有时我们不得不牺牲真正的无状态性。
JustAMartin

2

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

用户名/密码(在“授权”标头上发送)通常会保留在数据库中,以识别用户。有时,用户可能意味着另一个应用程序。但是,用户名/密码绝不用于标识特定的Web客户端用户代理。基于Web前端未创建或维护任何STATE 信息的Web代理与服务器之间基于在Authorization标头中使用用户名/密码(遵循HTTP基本授权)的对话为STATELESS。代表特定Web客户端用户代理的任何内容。并且基于我对REST的理解,该协议明确指出客户端与服务器之间的对话应为无状态。因此,如果我们想拥有真正的RESTful服务,则应该在每个调用中使用Authorization标头中的用户名/密码(请参阅我之前的文章中提到的RFC),而不是使用感觉类型的令牌(例如,在Web服务器中创建的Session令牌) ,在授权服务器中创建的OAuth令牌等)。

我了解到,一些称为REST的提供程序正在使用OAuth1或OAuth2接受令牌之类的令牌,以在HTTP标头中作为“ Authorization:Bearer”传递。但是,在我看来,将这些令牌用于RESTful服务将违反REST所包含的真正的STATELESS含义;因为这些令牌是在服务器端创建/维护的临时数据,用于在该Web客户端/服务器会话的有效期间内标识特定的Web客户端用户代理。因此,如果我们希望坚持使用STATELESS协议的TRUE含义,那么使用这些OAuth1 / 2令牌的任何服务都不应称为REST。

鲁本斯

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.