RESTful Web服务中的身份验证

12
我正在创建一个网站,用户可以查看和修改他们的小部件。所有与小部件数据的交互都将通过RESTful Web服务在我的服务器上完成。例如,如果用户想要查看其小部件列表,则执行流程如下:
  1. 用户12345访问https://www.example.com/Login.htm并通过服务器进行身份验证(在我的情况下通过OpenID提供者)
  2. 然后,用户12345访问页面https://www.example.com/Widgets.htm
  3. 服务器响应一个HTML页面和JavaScript将用于访问我的Web服务。
  4. 当HTML页面加载后,将调用JavaScript函数getWidgets()getWidgets()将调用我的Web服务https://www.example.com/Services/Widget/12345
  5. 该服务响应用户小部件的列表,另一个JavaScript函数renderWidgets(widgets)将更新HTML页面。
我不希望除用户12345之外的任何人访问其自己的小部件,因此我认为getWidgets()将需要向我的Web服务提供一些身份验证。我不确定实现这一点的最佳方法是什么。
我考虑客户端和服务器可以共享一个秘密,getWidgets()将向Web服务发送该秘密。服务器可以生成此秘密作为随机字符串(数字,GUID或任何内容),并在客户端请求初始HTML页面时将其包含在响应标头中。客户端将在向服务器发送请求时使用此秘密键。
这听起来合理吗?
这是一个常见的要求,因此有没有实现相同功能的标准方法?据我所知,这超出了OpenID的范围,OAuth也不适用。
提前感谢。

我还在考虑这个!... 这里有几个链接可以考虑 https://www.stormpath.com/blog/designing-rest-json-apis 和 https://www.stormpath.com/blog/secure-your-rest-api-right-way - Kevin Brydon
3个回答

9
这是一个很好的问题,但我认为你的解决方案可能需要比你想象的更加复杂。
通常,您希望验证这种情况的方式是两步握手。第一步是应用程序提供服务器一个私钥(由服务器生成,对客户端应用程序唯一)来验证它是否是有效的客户端。这就是为什么能够向服务器提供权威证据,说明请求来自已知且可信任的软件。
然后,第二步是当用户登录到您的客户端应用程序时,他们提供一个用户名/密码组合。此信息以及您的应用程序密钥应通过SSL发送到服务器。
SSL加密数据,以便具有数据包嗅探器的第三方无法在传输中读取数据,并且服务器执行以下操作:
1. 检查应用程序密钥是否有效。 2. 验证用户名是否存在,并与应用程序相关联。 3. 加密密码并将加密版本与与用户名关联的数据库中的加密版本进行测试。 4. 如果上述所有检查都通过,则服务器返回会话ID,该ID可以放入客户端cookie中,并用于在每个后续请求上重新验证用户。如果任何测试失败,则服务器返回401:未经授权或其他类似的错误。
此时,客户端可以利用返回的会话ID而无需继续重新提交应用程序密钥。
现在,在您的情况下,您可能实际上正在同一应用程序和同一服务器上托管客户端/服务器。在这种情况下,您通常可以跳过所有涉及私有应用程序密钥的部分,并简单地禁止跨站点脚本请求。
为什么?-因为您真正要保护的是以下内容:
服务器A托管您的RESTful API。 客户端B、C和D托管将依赖于服务器A的API的客户端。您不希望客户端E(不是您的应用程序且具有恶意)通过绕过或窃取其他客户端的凭据来访问服务器A。
但是,如果客户端和服务器都托管在同一个位置,并且因此具有相同的URL,即RESTful API位于www.yourdomain.com/api,客户端位于www.yourdomain.com/,则通常可以仅允许源自yourdomain.com之外的任何AJAX类型请求-并且这是您的安全层。
在这种情况下,以下是您需要做的一切以获得合理的安全级别:
  1. 为您的服务器启用SSL。
  2. 仅允许通过SSL来访问/auth/login(或任何您的登录POST方法)的请求(在C#中,可以通过在方法或控制器上使用[RequireHttps]属性来实现此操作)。
  3. 拒绝任何源自您自己域之外的AJAX请求。
  4. 在您的cookie中使用一层加密。

您的cookie应包含什么内容?

理想情况下,cookie应包含只有您的服务器才能解密的双向加密数据。换句话说-您可以将类似于用户的usernameuser_id之类的信息放入cookie中-但使用Rijndael或其他加密系统进行双向加密-使用只有您的服务器可以访问的加密密码(我建议使用随机字符串)。

然后-当您收到带有cookie附加的后续请求时,只需执行以下操作:

  1. 如果cookie存在,请尝试使用您的私有密码对其进行解密。
  2. 如果结果解密的数据是垃圾-抛出401:未经授权响应(这是更改或虚假cookie)
  3. 如果结果解密的数据是与您的数据库匹配的用户名-则现在知道谁正在发出请求-并可以相应地过滤/为其提供数据。

我希望这有所帮助。 :)如果没有-请随时发布任何评论并提问,我会尽力澄清。


2
谢谢你的回答。我需要一些时间来考虑,然后再回复你。 - Kevin Brydon
慢慢来 - 我现在正在为工作做一个非常类似的东西,所以它肯定是我脑海中的重点。 - Troy Alford
1
我已经授予您赏金,因为您是第一个发表评论的人,而且似乎有几个人同意您的观点。然而,我仍然认为我没有得到完整的答案。目前我有自己的解决方案(希望今晚能添加到答案中),但我还在等待有人说“嘿,这已经做过了,看看这个标准”。 - Kevin Brydon
好的,感谢你额外赞了我的回答,非常感激。我最终在工作项目中实现了几乎与我在这里回答的相同的东西,并进行了一些微调,就足以通过一些相当严格的安全要求了。不过,设置它需要一些工作量。如果你需要的话,我很乐意为你提供更多详细信息,或者在Skype上讨论什么的。只要在评论中告诉我,我会给你我的联系方式。 - Troy Alford

2
这难道不就是将请求发送给 Web 服务进行 MAC 处理吗?
因此,您提供调用 Web 服务的 JavaScript,在该 JavaScript 中放置一个 nonce。与大多数 nonce 实现不同,您将此实时保留为用户会话的持续时间。
在调用 Web 服务时,GetWidgets() 中的 JavaScript 使用 nonce 作为散列一些“随机”数据的密钥,我可能会使用格式化为字符串的时间和数据,用户的 ID(12345)作为盐。然后,JavaScript 将散列数据和未散列的时间字符串作为 Web 服务调用的一部分发送,但不包括 nonce。然后,我会确保发送的时间是最近的(即最近 20 秒,限制重放攻击),并且当服务器将时间与用户 ID 作为盐与它设置的 nonce 散列(因为它知道它)时,它得到相同的结果。
因此,我们使用了由服务器设置并发送到客户端以充当生成散列消息授权码(MAC 或 HMAC)的密钥,但还防止了重放攻击(每个请求的 MAC 都将不同,并具有非常短的重放窗口),并通过不在 cookie 中传输任何会话信息来防止会话劫持。
我以前没有遇到过你的具体情况,但似乎这是一个通用问题的特殊案例,即在不想在每个消息中发送凭据的情况下验证消息。MACing 是我见过的可靠解决方法。
参见:http://en.wikipedia.org/wiki/Message_authentication_code,它对 MACing 给出了很好的概述,甚至包括一个漂亮的图表来帮助解释。这是一个相当成熟的解决方案,唯一建议偏离的地方(因为我曾经犯过错误)是在消息中包含时间以防止重放攻击。
这是 Last.fm 用来帮助授权访问其 API 的方法基础,参见该页面的第 8 部分(Signing Calls):http://www.last.fm/api/authspec#8。他们使用的哈希函数是 MD5,查询字符串被包含在要进行哈希的正文中,而密钥则是从初始身份验证调用中获取的会话密钥。

谢谢回复。你知道有哪些可信的来源可以描述这个问题吗?我不相信我是唯一遇到这个问题的人,而且我期望解决方案已经在某处有所记录。 - Kevin Brydon
我本来打算将悬赏奖励授予你和特洛伊,但好像只能给一个人(是这样吗?)。感谢提供的信息,一旦思路整理好了,我将在答案中添加我的解决方案(但不标记为答案)。根据我刚刚在特洛伊的答案中留下的评论,我希望有人可以指引我找到适当的标准实现方向。 - Kevin Brydon

-1

我对安全方面了解不多,但我认为这完全取决于您愿意花费多少时间/成本(请记住,一切都可以被黑客攻击)。

如果您很关注安全性,您可能已经保护了您的会话变量,您可以做的最简单的事情是进行一个ajax调用到服务器操作,在其中检查会话并将其与用户请求进行比较。


2
感谢您抽出时间回复,但我认为您的答案并没有为讨论增添任何内容。关于您的第一点,是的,任何安全措施在足够的时间和努力下都可以被破解。目标应该是拥有一个解决方案,在其中所需的时间和努力超过了回报。对于您的第二点,RESTful Web服务不应依赖会话来保留状态。这是一个核心原则。参考http://www.peej.co.uk/articles/no-sessions.html。 - Kevin Brydon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接