ChatGPT解决这个技术问题 Extra ChatGPT

REST API 基于令牌的身份验证

我正在开发一个需要身份验证的 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_keytimestampverifier。 “验证者”由以下方式返回:

sha1(timestamp + api_key + shared_secret)

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

这够好吗?杀戮?矫枉过正?

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

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

对于可能的最简单的调用,这似乎非常冗长。考虑到 shared_secret 最终会嵌入(至少)一个 iOS 应用程序中,我认为可以从中提取它,这是否甚至提供了超出虚假安全感的任何东西?

而不是使用 sha1(timestamp+api_key+shard_secret) 您应该使用 hmac(shared_secret, timmpestamp+api_key) 以获得更好的安全散列 en.wikipedia.org/wiki/Hash-based_message_authentication_code
@MiguelA.Carrasco 似乎在 2017 年达成共识,认为 bCrypt 是新的哈希工具。

c
cmc

让我将所有内容分开并单独解决每个问题:

验证

对于身份验证,baseauth 的优势在于它是协议层面的成熟解决方案。这意味着很多“以后可能会出现”的问题已经为您解决了。例如,使用 BaseAuth,用户代理知道密码是密码,因此他们不会缓存它。

验证服务器负载

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

传输安全

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

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

如果这是不可能的,并且传输的信息不是秘密的,我建议使用哈希来保护请求,正如您在令牌方法中所建议的那样。由于哈希提供了安全性,您可以指示您的用户提供哈希作为 baseauth 密码。为了提高鲁棒性,我建议使用随机字符串而不是时间戳作为“nonce”来防止重放攻击(可以在同一秒内发出两个合法请求)。无需提供单独的“共享密钥”和“api 密钥”字段,您可以简单地将 api 密钥用作共享密钥,然后使用不会更改的盐来防止彩虹表攻击。用户名字段似乎也是放置随机数的好地方,因为它是身份验证的一部分。所以现在你有一个像这样的干净调用:

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 服务,那么最好在目标操作系统上找到某种密钥环 API 并让 SDK(或实现者)存储那里的钥匙。如果这是不可能的,您至少可以通过对其进行加密并将加密的数据和加密密钥存储在不同的位置来使获取秘密变得更加困难。

如果您试图阻止其他软件供应商获取您的 API 密钥以防止开发备用客户端,那么只有加密和存储分离的方法几乎可以工作。这是白盒加密,迄今为止,还没有人提出真正安全的解决方案来解决此类问题。您至少可以为每个用户颁发一个密钥,这样您就可以禁止滥用密钥。

(*) 编辑: SSL 连接 should no longer be considered secure 没有 taking additional steps to verify 它们。


感谢 cmc,所有优点和值得深思的食物。我最终采用了一种类似于您上面讨论的令牌/HMAC 方法,就像 S3 REST API 身份验证机制一样。
如果您将令牌缓存在服务器上,那么它与旧的会话 id 本质上不一样吗?会话 id 是短暂的,它还附加到快速缓存存储(如果您实现它),以避免在每个请求上访问您的数据库。真正的 RESTful 和无状态设计不应该有会话,但是如果您使用令牌作为 ID,然后仍然访问数据库,那么使用会话 ID 不是更好吗?或者,您可以使用 JSON Web 令牌,其中包含整个会话数据的加密或签名信息,以实现真正的无状态设计。
R
Rubens Gomes

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

对于 HTTP,RESTful API 应符合现有的 HTTP 标准标头。添加新的 HTTP 标头违反了 REST 原则。不要重新发明轮子,使用 HTTP/1.1 标准中的所有标准特性——包括状态响应代码、标头等。 RESTFul Web 服务应该利用和依赖 HTTP 标准。 RESTful 服务必须是无状态的。任何技巧,例如尝试记住服务器上先前 REST 请求状态的基于令牌的身份验证,都违反了 REST 原则。同样,这是必须的;也就是说,如果您的 Web 服务器在服务器上保存任何与请求/响应上下文相关的信息以尝试在服务器上建立任何类型的会话,那么您的 Web 服务不是无状态的。如果它不是无状态的,它就不是 RESTFul。

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

我为 Cisco Prime Performance Manager 应用程序开发了一个 RESTful 服务。在 Google 中搜索我为该应用程序编写的 REST API 文档,以了解有关 RESTFul API 合规性的更多详细信息here。在那个实现中,我选择使用 HTTP“基本”授权方案。 - 查看该 REST API 文档的 1.5 或更高版本,并在文档中搜索授权。


“添加新的 HTTP 标头违反了 REST 原则”怎么会?如果你在这方面,你可能会很好地解释在一段时间后过期的密码和在一段时间后过期的令牌之间究竟有什么区别(关于原则)。
用户名 + 密码是一个令牌(!),在每次请求时在客户端和服务器之间交换。该令牌在服务器上维护并具有生存时间。如果密码过期,我必须获得一个新密码。您似乎将“令牌”与“服务器会话”相关联,但这是一个无效的结论。它甚至是无关紧要的,因为这将是一个实现细节。恕我直言,您将用户名/密码以外的令牌分类为有状态纯粹是人为的。
我认为您应该激励为什么要使用 RESTful over Basic Authentication 来实现,这是原始问题的一部分。也许您还可以链接到一些包含代码的好示例。作为这个主题的初学者,理论似乎很清楚,有很多好的资源,但实现方法不是很清楚,例子很复杂。我感到沮丧的是,似乎需要自定义编码才能及时实现已经完成数千次的事情。
-1 “任何技巧,例如尝试记住服务器上先前 REST 请求状态的基于令牌的身份验证,都违反了 REST 原则。”基于令牌的身份验证与先前 REST 请求的状态无关,并且不违反 REST 的无状态性。
那么,据此,JSON Web Tokens 是违反 REST 的,因为它们可以存储用户的状态(声明)?无论如何,我更喜欢违反 REST 并使用良好的旧会话 ID 作为“令牌”,但初始身份验证是使用用户名+密码执行的,使用共享密钥和非常短暂的时间戳进行签名或加密(因此,如果有人试图重播,它会失败那)。在“企业级”应用程序中,很难放弃会话优势(避免访问数据库以获取几乎每个请求所需的某些数据),因此有时我们必须牺牲真正的无状态性。
6
6 revs

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

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

我知道几个被称为 REST 的提供商正在使用 OAuth1 或 OAuth2 接受令牌等令牌作为 HTTP 标头中的“授权:承载”传递。但是,在我看来,将这些令牌用于 RESTful 服务会违反 REST 所包含的真正 STATELESS 含义;因为这些令牌是在服务器端创建/维护的临时数据片段,用于在该 Web 客户端/服务器对话的有效持续时间内识别特定的 Web 客户端用户代理。因此,如果我们想坚持 STATELESS 协议的真正含义,则任何使用这些 OAuth1/2 令牌的服务都不应称为 REST。

鲁本斯