REST 和身份验证变体

Posted

技术标签:

【中文标题】REST 和身份验证变体【英文标题】:REST and authentication variants 【发布时间】:2010-10-02 06:57:46 【问题描述】:

我目前正在为 .net 开发一个 REST 库,我想听听一些关于我的开放点的意见:REST 和身份验证。

以下是与库一起使用的 RESTful 接口示例:

[RestRoot("/user")]
public interface IUserInterface

  [RestPut("/")]
  void Add(User user);

  [RestGet("/")]
  int[] List();

  [RestGet("/get/id")]
  User Get(int id);

  [RestDelete("/delete/id")]
  void Delete(int id);

然后服务器代码只实现接口,客户端可以通过工厂获得相同的接口。或者,如果客户端不使用该库,则标准 HTTP 请求也可以工作。

我知道使用 HTTP Basic Auth 或向需要经过身份验证的用户的请求发送令牌的主要方法。

第一种方法(HTTP 基本身份验证)存在以下问题(部分特定于 Web 浏览器):

每次请求都会传输密码 - 即使使用 SSL,这也有某种“不好的感觉”。 由于密码是通过请求头传输的,本地攻击者很容易通过查看传输的头来获取密码。 密码在浏览器内存中可用。 没有使用户“会话”过期的标准方法。 使用浏览器登录会中断页面​​的外观。

第二种方法的问题更侧重于实现和库的使用:

每个需要认证的请求URI都必须有一个token的参数,这只是非常重复。 如果每个方法实现都需要检查令牌是否有效,则需要编写更多代码。 界面将变得不那么具体,例如[RestGet("/get/id")][RestGet("/get/id/token")]。 在哪里放置令牌:在 URI 的末尾?根之后?其他地方?

我的想法是将令牌作为参数传递给 URL,例如 http:/server/user/get/1234?token=token_id

另一种可能性是将参数作为 HTTP 标头发送,但我猜这会使普通 HTTP 客户端的使用变得复杂。

令牌会在每个请求中作为自定义 HTTP 标头(“X-Session-Id”)传回客户端。

这可以从接口中完全抽象出来,任何需要身份验证的实现都可以只询问令牌(如果给定的话)属于哪个用户。

您认为这是否会过多地违反 REST 或者您有什么更好的想法?

【问题讨论】:

【参考方案1】:

我倾向于认为身份验证详细信息属于标头,而不是 URI。如果您依赖放置在 URI 上的令牌,那么您的应用程序中的每个 URI 都需要进行编码以包含该令牌。它还会对缓存产生负面影响。具有不断变化的令牌的资源将不再能够被缓存。资源相关信息属于 URI,而不是凭据等应用程序相关数据。

您似乎必须将 Web 浏览器作为客户端?如果是这样,您可以使用HTTP Digest access authentication 进行调查或向客户端颁发他们自己的 SSL 证书以唯一地识别和验证它们。另外,我认为会话 cookie 不一定是坏事。特别是在必须处理浏览器时。只要您隔离 cookie 处理代码并使应用程序的其余部分不依赖它,您就可以了。关键只是在会话中存储用户的身份,没有别的。不要滥用服务器端会话状态。

如果您的目标客户不是浏览器,那么您可以采取多种方法。我很幸运地使用了 Amazon 的 S3 Authentication 机制。

当然,这都是非常主观的。纯度和严格遵循 REST 有时可能是不切实际的。只要您最小化和隔离此类行为,您的应用程序的核心仍然可以是 RESTful。我强烈推荐 RESTful Web Services 作为 REST 信息和方法的重要来源。

【讨论】:

在 S3 示例中,他们与客户端共享加密密钥 - 这很糟糕,不是吗? 与任何类型的共享秘密密码术一样,双方都必须保持密钥的保密性。在这方面,对称密码学并不差。【参考方案2】:

我同意 workmad3,如果需要维护会话生命周期,您应该创建一个会话资源。使用用户凭据(基本身份验证或正文内容中的凭据)在该资源上发布将返回唯一的会话 ID。在 /session/id 上删除将注销用户。

如果你想控制会话过期时间。当创建新会话(在会话资源上发布)时,服务器将在响应上设置一个 cookie(使用标准的 set-cookie 标头)。 cookie 将包含到期时间。 cookie 字符串应该在服务器上加密,因此只有服务器可以打开该 cookie。 对服务器的每个后续请求都将在 cookie 标头中发送会话 cookie。 (如果您的客户端是浏览器,它将自动为您完成)。服务器需要为每个请求“更新”cookie,即创建具有新到期时间的新 cookie(延长会话的超时)。 记得在用户对会话资源调用 delete 时清除 cookie。

如果您希望您的应用程序更安全,您可以将客户端 IP 存储在 cookie 本身中,这样当请求到达时,服务器可以验证它是从“原始”客户端发送的。但请记住,当涉及代理时,此解决方案可能会出现问题,因为服务器可能会“看到”所有请求都来自同一个客户端。

【讨论】:

我没有看到这里的代理有问题。只是某些客户端看起来相同,这降低了增加的安全性。 cookie 也应该是httpOnly。【参考方案3】:

我见过的其余身份验证将会话视为用于创建、销毁等的 REST 资源,然后来回传递会话 ID。我见过的人倾向于为此使用会话 cookie,因为这是真正保护它的唯一方法。如果您在 URL 中传递会话 ID,则无法真正验证它来自正确的客户端。

但是,身份验证是 REST 的一个棘手问题,因为它需要将某种形式的状态保留在 URL 之外,这违反了 REST 原则,即 URL 是表示状态所需的全部内容。

【讨论】:

因为如果不在安全网络内部使用,它将始终使用 SSL,并且使用 UUID 进行会话,因此它的来源应该不是问题。是什么阻碍了人们伪造 cookie? 我不会说 URI 是表示状态所需的全部内容是 REST 的原则。相反,让资源可寻址并且请求中包含的所有应用程序状态似乎是一种更好的声明方式。 您可以使用 MAC 保护 cookie,例如服务器有一个秘密,并将向客户端分发以下会话令牌: _ 其中 mac 构造为 SHA1(_) 当客户端发送令牌时 _ 服务器必须检查 _ 是否等于 在这些讨论中,区分应用程序状态(会话状态)和资源状态非常重要。在 RESTful 架构中,资源状态存储在服务器端,而应用程序状态存储在客户端。 [infoq.com/articles/mark-baker-hypermedia] @maaartinus:目前(正如“前段时间”提出的问题)我不会再考虑使用任何会话数据作为 URL 的一部分,首先这似乎是合乎逻辑的,但从第二个角度来看,风险太高了。

以上是关于REST 和身份验证变体的主要内容,如果未能解决你的问题,请参考以下文章

REST API 的身份管理和身份验证

使用 JWT 联合身份的 REST 身份验证/授权

Spring Data REST 中的身份验证和授权

通过 SSL 和 HTTP 基本身份验证的 REST 服务

Intranet API 的 REST 身份验证缓存

REST 安全性,使用基本身份验证和 jwt 令牌验证是一种不好的做法吗?