RESTful身份验证

RESTful身份验证是什么意思?它是如何工作的?我在Google上找不到一个很好的概述。我唯一的理解是你在URL中传递会话密钥(记忆),但这可能是非常错误的。

422831 次浏览

它当然与“会话密钥”无关,因为它通常用于指在REST的所有约束范围内执行的无会话身份验证。每个请求都是自描述的,携带足够的信息来自行授权请求,而无需任何服务器端应用程序状态。

最简单的方法是从rfc2617中的HTTP内置身份验证机制开始。

以我的理解来回答这个问题…

一个使用REST的身份验证系统,这样你就不需要实际跟踪或管理系统中的用户。这是通过使用HTTP方法POST、GET、PUT、DELETE来完成的。我们采用这4种方法,并将它们视为数据库交互方面的CREATE、READ、UPDATE、DELETE(但在网络上我们使用POST和GET,因为这是锚标签目前支持的)。因此,将POST和GET视为我们的CREATE/READ/UPDATE/DELETE(CRUD),然后我们可以在网络应用程序中设计路由,这些路由将能够推断我们正在实现的CRUD的操作。

例如,在Ruby on Rails应用程序中,我们可以构建我们的Web应用程序,如果登录的用户访问http://store.com/account/logout,则该页面的GET可以被视为试图注销的用户。在我们的rails控制器中,我们将构建一个操作,将用户注销并将他们发送回主页。

登录页面上的GET将产生一个表单。登录页面上的POST将被视为登录尝试并获取POST数据并使用它来登录。

对我来说,这是一种使用映射到其数据库含义的HTTP方法的做法,然后构建一个身份验证系统,您不需要传递任何会话ID或跟踪会话。

我还在学习--如果你发现我说的任何错误,请纠正我,如果你学到更多,请在这里发布。谢谢。

我认为RESTful身份验证涉及在请求中传递身份验证令牌作为参数。示例是api使用apikey。我不认为使用cookie或超文本传输协议auth符合条件。

我怀疑那些热情地喊着“HTTP身份验证”的人是否曾经尝试过使用REST制作基于浏览器的应用程序(而不是机器对机器的Web服务)(无意冒犯-我只是不认为他们曾经面临过并发症)。

我发现在生成要在浏览器中查看的超文本标记语言页面的RESTful服务上使用HTTP身份验证的问题是:

  • 用户通常会得到一个丑陋的浏览器制作的登录框,这对用户非常不友好。您不能添加密码检索、帮助框等。
  • 注销或以不同的名称登录是一个问题-浏览器将继续向站点发送鉴别信息,直到您关闭窗口
  • 超时很困难

一篇非常有洞察力的文章逐点解决了这些问题这里,但这导致了浏览器特定的javascript黑客、变通方法等很多。因此,它也不是向前兼容的,因此随着新浏览器的发布需要不断维护。我不认为那是干净清晰的设计,而且我觉得这是很多额外的工作和头痛,只是为了让我能热情地向我的朋友展示我的REST徽章。

我相信cookie是解决方案。但是等等,cookie是邪恶的,不是吗?不,它们不是,cookie经常使用的方式是邪恶的。cookie本身只是一段客户端信息,就像浏览器在您浏览时会跟踪的HTTP身份验证信息一样。这条客户端信息在每次请求时都会发送到服务器,就像HTTP身份验证信息一样。从概念上讲,唯一的区别是这条客户端状态的内容可以由服务器确定,作为其响应的一部分。

通过使会话成为RESTful资源,只需以下规则:

  • 会话将键映射到用户ID(可能还有用于超时的last-action-timemark)
  • 如果存在会话,则表示密钥有效。
  • 登录意味着发布到 /sessions,新密钥设置为cookie
  • 注销意味着删除 /sessions/{key}(过载的POST,记住,我们是一个浏览器,超文本标记语言5还有很长的路要走)
  • 身份验证是通过在每次请求中将密钥作为cookie发送并检查会话是否存在和有效来完成的

现在,与HTTP身份验证的唯一区别是身份验证密钥由服务器生成并发送给不断发送回的客户端,而不是客户端从输入的凭据中计算它。

转换器42补充说,当使用https(我们应该)时,重要的是cookie将设置其安全标志,以便身份验证信息永远不会通过非安全连接发送。太好了,我自己没见过。

我觉得这是一个很好的解决方案,但我必须承认,我不足以成为一个安全专家来识别这个方案中的潜在漏洞——我所知道的是,数百个非RESTful的Web应用程序使用基本上相同的登录协议(PHP中的_SESSION美元,JavaEE中的HttpSession等)。cookie头内容只是用于寻址服务器端资源,就像接受语言用于访问翻译资源一样。我觉得是一样的,但也许其他人不这样认为?伙计们,你们怎么看?

如何在RESTful Client-Server架构中处理身份验证是一个有争议的问题。

通常,在SOA over HTTP世界中,可以通过以下方式实现:

  • 基于HTTPS的HTTP基本认证;
  • Cookie和会话管理;
  • HTTP标头中的令牌(例如OAuth 2.0+JWT);
  • 使用附加签名参数查询身份验证。

您必须适应,甚至更好地混合这些技术,以最好地匹配您的软件架构。

每个身份验证方案都有自己的优点和缺点,具体取决于您的安全策略和软件架构的目的。

基于HTTPS的HTTP基本认证

第一个解决方案基于标准HTTPS协议,被大多数Web服务使用。

GET /spec.html HTTP/1.1Host: www.example.orgAuthorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

它很容易实现,默认情况下可在所有浏览器上使用,但有一些已知的缺点,例如浏览器上显示的可怕的身份验证窗口,它将持续存在(这里没有类似LogOut的功能),一些服务器端额外的CPU消耗,以及用户名和密码被传输(通过HTTPS)到服务器的事实(在键盘输入期间,让密码仅保留在客户端并作为安全哈希存储在服务器上应该更安全)。

我们可以使用摘要式认证,但它也需要HTTPS,因为它容易受到MiM重播攻击,并且特定于HTTP。

会话通过Cookies

老实说,在服务器上管理的会话并不是真正的无状态。

一种可能是维护cookie内容中的所有数据。而且,根据设计,cookie是在服务器端处理的(事实上,客户端甚至不会尝试解释此cookie数据:它只是在每次连续请求中将其返回给服务器)。但此cookie数据是应用程序状态数据,因此在纯无状态世界中应该由客户端而不是服务器来管理它。

GET /spec.html HTTP/1.1Host: www.example.orgCookie: theme=light; sessionToken=abc123

cookie技术本身是HTTP链接的,所以它不是真正的RESTful,应该是独立于协议的,IMHO。它容易受到MiM重播攻击。

通过代币(OAuth2)授予

另一种方法是在HTTP标头中放置一个令牌,以便对请求进行身份验证。例如,这就是OAuth 2.0所做的。参见rfc6749

 GET /resource/1 HTTP/1.1Host: example.comAuthorization: Bearer mF_9.B5f-4.1JqM

简而言之,这与cookie非常相似,并且存在相同的问题:不是无状态的,依赖于HTTP传输详细信息,并且受很多安全漏洞(包括MiM和Replay)的约束,因此只能在HTTPS上使用。通常,智威汤逊用作令牌。

查询认证

查询身份验证包括通过URI上的一些附加参数对每个RESTful请求进行签名。请参阅这篇参考文章

它在这一条中被定义为:

所有REST查询都必须通过签名查询参数进行身份验证使用私人凭据按小写字母顺序排序作为签名令牌。签名应在URL编码之前发生查询字符串。

这种技术可能与无状态架构更兼容,也可以通过轻量级会话管理(使用内存会话而不是DB持久性)来实现。

例如,这是上面链接中的一个通用URI示例:

GET /object?apiKey=Qwerty2010

应按以下方式发送:

GET /object?timestamp=1261496500&apiKey=Qwerty2010&signature=abcdef0123456789

要签名的字符串是/object?apikey=Qwerty2010&timestamp=1261496500,签名是使用API密钥的私有组件的该字符串的SHA256哈希。

服务器端数据缓存可以始终可用。例如,在我们的框架中,我们在SQL级别而不是URI级别缓存响应。因此添加这个额外的参数不会破坏缓存机制。

有关基于JSON和REST的客户端-服务器ORM/SOA/MVC框架中RESTful身份验证的一些详细信息,请参阅这篇文章。由于我们不仅允许通过HTTP/1.1进行通信,还允许命名管道或GDI消息(本地),我们尝试实现真正的RESTful身份验证模式,而不依赖于HTTP特异性(如标头或cookie)。

后来的笔记:在URI中添加签名可能被视为不好的做法(因为例如它会出现在超文本传输协议服务器日志中),因此必须减轻它,例如通过适当的TTL来避免重播。但是,如果您的超文本传输协议日志受到损害,您肯定会遇到更大的安全问题。

在实践中,即将到来的OAuth 2.0的MAC令牌身份验证可能是对“令牌授予”当前方案的巨大改进。但这仍然是一个半成品,与HTTP传输相关。

结论

值得得出的结论是,REST不仅基于HTTP,即使在实践中,它也主要通过HTTP实现。REST可以使用其他通信层。因此,无论谷歌回答什么,RESTful身份验证不仅仅是HTTP身份验证的同义词。它甚至不应该使用HTTP机制,而是应该从通信层抽象出来。如果你使用HTTP通信,多亏了让我们加密倡议,没有理由不使用正确的HTTPS,这是任何身份验证方案都需要的。

这就是这样做的方式:使用OAuth 2.0登录

您可以使用Google以外的其他身份验证方法,只要它支持OAuth。

这篇“非常有见地”的文章由@sk的提到(http://www.berenddeboer.net/rest/authentication.html)讨论了一种复杂但真正破碎的身份验证方法。

您可以尝试在没有任何登录凭据的情况下访问页面(应该仅对经过身份验证的用户可见)http://www.berenddeboer.net/rest/site/authenticated.html

(对不起,我不能对答案发表评论。

我想说REST和身份验证根本不能混合。REST意味着无状态,但“经过身份验证”是一种状态。你不能将它们放在同一层。如果你是RESTful的倡导者并且不赞成状态,那么你必须使用HTTPS(即将安全问题留给另一层)。

首先,RESTful Web服务是无状态(或者换句话说,无休止)。因此,RESTful服务没有也不应该涉及会话或cookie的概念。在RESTful服务中进行身份验证或授权的方法是使用RFC 2616 HTTP规范中定义的HTTP授权标头。每个请求都应该包含HTTP授权标头,并且请求应该通过HTTPs(SSL)连接发送。这是在HTTP RESTful Web服务中进行身份验证和验证请求授权的正确方法。我已经为Cisco Systems的Cisco PRIME性能管理器应用程序实现了RESTful Web服务。作为该Web服务的一部分,我也实现了身份验证/授权。

老实说,我在这里看到了很好的答案,但让我有点困扰的是,当有人将整个无状态概念带到极端时,它变得教条主义。这让我想起了那些老Smalltalk粉丝,他们只想拥抱纯粹的OO,如果某物不是对象,那么你就做错了。饶了我吧。

RESTful方法应该让你的生活更轻松,减少会话的开销和成本,试着遵循它,因为这是一件明智的事情,但是一旦你遵循一门学科(任何学科/指南)到极限,它不再提供预期的好处,那么你就错了。今天一些最好的语言同时拥有函数式编程和面向对象。

如果您解决问题的最简单方法是将身份验证密钥存储在cookie中并以HTTP标头发送,那么就这样做,只是不要滥用它。请记住,当会话变得又重又大时,会话是不好的,如果您的会话只包含一个包含密钥的短字符串,那么有什么大不了的?

我愿意接受评论中的更正,但我只是不明白(到目前为止)让我们的生活变得痛苦的意义,只是为了避免在我们的服务器上保留一个大的哈希字典。

这是一个真正且完全RESTful的身份验证解决方案:

  1. 在身份验证服务器上创建公钥/私钥对。
  2. 将公钥分发到所有服务器。
  3. 当客户端进行身份验证时:

    3.1.发行一个包含以下内容的令牌:

    • 到期时间
    • 用户名(可选)
    • 用户IP(可选)
    • 密码的哈希值(可选)

    3.2.用私钥加密令牌。

    3.3.将加密令牌发送回用户。

  4. 当用户访问任何API时,他们还必须传入他们的身份验证令牌。

  5. 服务器可以通过使用身份验证服务器的公钥解密令牌来验证令牌是否有效。

这是无状态/RESTful身份验证。

请注意,如果包含密码哈希,用户还将发送未加密的密码以及身份验证令牌。服务器可以通过比较哈希来验证密码是否与用于创建身份验证令牌的密码匹配。需要使用HTTPS之类的安全连接。客户端的Javascript可以处理获取用户密码并将其存储在客户端的内存或cookie中,可能使用服务器的公共密钥进行加密。

使用公钥基础设施,其中密钥的注册涉及适当的绑定,确保公钥以确保不可否认的方式绑定到分配给它的个人

请参见http://en.wikipedia.org/wiki/Public_key_infrastructure。如果您遵循适当的PKI标准,可以识别出不正当使用被盗密钥的人或代理人并将其锁定。如果代理人被要求使用证书,绑定会变得非常紧密。一个聪明且行动迅速的小偷可以逃脱,但他们会留下更多的面包屑。

关于这个话题,在座的好人已经说得够多了。但这是我的两分钱。

有2种交互模式:

  1. 人对机器(HTM)
  2. 机器对机器(MTM)

机器是公分母,表示为REST API,参与者/客户端是人类或机器。

现在,在真正的RESTful架构中,无状态的概念意味着所有相关的应用程序状态(意味着客户端状态)必须与每个请求一起提供。相关的意思是REST API处理请求和提供适当响应所需的任何东西。

当我们在人对机器应用程序的上下文中考虑这一点时,“基于浏览器的”,正如斯克雷贝尔在上面指出的那样,这意味着在浏览器中运行的(Web)应用程序需要将其状态和相关信息与它发出的每个请求一起发送到后端REST API。

考虑一下:你有一个REST API的数据/信息平台公开资产。也许你有一个处理所有数据立方体的自助服务BI平台。但是你希望你的(人类)客户通过(1)网络应用程序,(2)移动应用程序和(3)一些第三方应用程序访问它。最终,即使是MTM链也会导致HTM——对。所以人类用户仍然处于信息链的顶端。

在前两种情况下,您有一个人机交互的案例,这些信息实际上是由人类用户使用的。在最后一种情况下,您有一个机器程序使用REST API。

身份验证的概念适用于所有领域。您将如何设计它,以便以统一、安全的方式访问您的REST API?在我看来,有两种方法:

方法一:

  1. 首先,没有登录。每个请求都执行登录
  2. 客户端发送其标识参数+请求特定每个请求的参数
  3. REST API获取它们,转身,ping用户存储(不管是什么)并确认身份验证
  4. 如果验证成立,则为请求提供服务;否则,拒绝使用适当的HTTP状态代码
  5. 对您的所有REST API中的每个请求重复上述操作曲库

方式2:

  1. 客户端以一个auth请求开始
  2. 登录REST API将处理所有此类请求
  3. 它接受身份验证参数(API密钥、uid/pwd或任何你需要的参数)选择)并根据用户存储(LDAP、AD或MySQL DB等)验证身份验证
  4. 如果验证,则创建一个身份验证令牌并将其返回给客户端/调用者
  5. 然后调用者发送此身份验证令牌+请求特定参数对其他业务REST API的每个后续请求,直到注销或租约到期

显然,在Way-2中,REST API需要一种方法来识别和信任令牌是有效的。登录API执行身份验证,因此“代客密钥”需要被曲库中的其他REST API信任。

当然,这意味着身份验证密钥/令牌需要在REST API之间存储和共享。这个共享的、受信任的令牌存储库可以是本地/联合的,从而允许来自其他组织的REST API相互信任。

但我离题了。

关键是,需要维护和共享一个“状态”(关于客户端经过身份验证的状态),以便所有REST API都可以创建一个信任圈。如果我们不这样做,这是方式1,我们必须接受必须对任何/所有传入的请求执行身份验证行为。

执行身份验证是一个资源密集型过程。想象一下,对每个传入的请求执行SQL查询,以检查uid/pwd匹配。或者,加密和执行哈希匹配(AWS风格)。从架构上讲,我怀疑每个REST API都需要使用公共后端登录服务来执行这一点。因为,如果你不这样做,那么你就会把身份验证代码扔得到处都是。一团糟。

所以更多的层,更多的延迟。

现在,以Way-1为例,应用于HTM。你的人类用户真的在乎你是否必须在每个请求中发送uid/pwd/hash之类的信息吗?不,只要你不要每秒抛出auth/登录页面来打扰她就行。如果你这样做了,祝你有客户好运。所以,你要做的是将登录信息存储在客户端的某个地方,在浏览器中,并在发出的每个请求时发送它。对于人类用户,她已经登录了,并且有一个会话可用。但实际上,她在每个请求上都是经过身份验证的。

与Way-2相同。您的(人类)用户永远不会注意到。所以没有造成伤害。

如果我们将Way-1应用于MTM呢?在这种情况下,由于它是一台机器,我们可以通过要求它提交每个请求的鉴别信息来让这个家伙感到无聊。没人在乎!在MTM上执行Way-2不会引起任何特殊反应;这是一台该死的机器。它可能不在乎!

所以,真正的问题是什么适合你的需要。无国籍是要付出代价的。付出代价,继续前进。如果你想成为一个纯粹主义者,那就付出代价,继续前进。

最后,哲学并不重要。真正重要的是信息发现、演示和消费体验。如果人们喜欢你的API,你就完成了你的工作。

2019年2月16日更新

下面前面提到的方法本质上是OAuth2.0的“资源所有者密码凭证”授权类型。这是一种启动和运行的简单方法。然而,使用这种方法,组织中的每个应用程序最终都将拥有自己的身份验证和授权机制。推荐的方法是“授权代码”授权类型。此外,在我之前的回答中,我推荐浏览器localStorage用于存储身份验证令牌。然而,我开始相信cookie是实现这一目的的正确选择。我在这个StackOverflow答案中详细说明了我的原因、授权代码授予类型实现方法、安全注意事项等。


我认为以下方法可用于REST服务身份验证:

  1. 创建一个登录RESTful API以接受用户名和密码进行身份验证。使用HTTP POST方法防止缓存和SSL以确保传输过程中的安全成功认证后,API返回两个JWT-一个访问令牌(较短的有效期,例如30分钟)和一个刷新令牌(较长的有效期,例如24小时)
  2. 客户端(基于Web的UI)将JWT存储在本地存储中,并且在每个后续的API调用中在“Authorization:承载#访问令牌”标头中传递访问令牌
  3. API通过验证签名和到期日期来检查令牌的有效性。如果令牌有效,请检查用户(它将JWT中的“子”声明解释为用户名)是否可以通过缓存查找访问API。如果用户有权限访问API,请执行业务逻辑
  4. 如果令牌过期,API返回HTTP响应代码400
  5. 客户端在接收到400/401时,调用另一个REST API,其中包含“Authorization: Beker#刷新令牌”标头中的刷新令牌以获取新的访问令牌。
  6. 收到使用刷新令牌的调用时,通过检查签名和到期日期检查刷新令牌是否有效。如果刷新令牌有效,请从DB刷新用户的访问权限缓存,并返回新的访问令牌和刷新令牌。如果刷新令牌无效,请返回HTTP响应代码400
  7. 如果返回新的访问令牌和刷新令牌,请转到步骤2。如果返回HTTP响应代码400,客户端假定刷新令牌已过期,并要求用户提供用户名和密码
  8. 对于注销,清除本地存储

使用这种方法,我们每30分钟执行一次昂贵的操作,即加载具有用户特定访问权限详细信息的缓存。因此,如果访问被撤销或授予新访问权限,则需要30分钟才能反映或注销然后登录。

适用于保护任何Web应用程序的提示

如果您想保护您的应用程序,那么你绝对应该从使用HTTPS而不是HTTP开始,这可确保在您和用户之间创建一个安全通道,以防止嗅探来回发送给用户的数据,并有助于保持交换的数据的机密性。

您可以使用JWT(JSON Web令牌)来保护RESTful API,这与服务器端会话相比有很多好处,好处主要是:

1-更具可扩展性,因为您的API服务器不必为每个用户维护会话(当您有许多会话时,这可能是一个很大的负担)

2-JWT是自包含的,有定义用户角色的声明,例如,他可以访问什么,在日期和到期日期发布(之后JWT将无效)

3-更容易处理负载均衡器,如果您有多个API服务器,因为您不必共享会话数据,也不必配置服务器将会话路由到同一服务器,无论何时带有JWT的请求击中任何服务器,都可以进行身份验证和授权

4-对数据库的压力更小,您不必为每个请求不断存储和检索会话ID和数据

5-如果您使用强密钥对JWT进行签名,则JWT不会被篡改,因此您可以信任随请求发送的JWT中的声明,而无需检查用户会话&无论他是否被授权,您只需检查JWT&然后您就可以知道该用户可以做谁和什么。

许多库提供了在大多数编程语言中创建和验证JWT的简单方法,例如:在node.js最流行的是JSONWebToken

由于REST API通常旨在保持服务器无状态,因此JWT更兼容该概念,因为每个请求都与自包含的授权令牌一起发送(JWT),而服务器不必跟踪用户会话,而会话使服务器有状态,以便它记住用户和他的角色,然而,会话也被广泛使用并有其优点,如果你愿意,你可以搜索。

需要注意的一件重要事情是,您必须使用HTTPS安全地将JWT交付给客户端并将其保存在安全的地方(例如在本地存储中)。

您可以了解更多关于JWTs从这个链接的信息