【问题标题】:RESTful authentication. Client-side, stateless unauthenticationRESTful 身份验证。客户端,无状态的未认证
【发布时间】:2012-08-29 09:08:06
【问题描述】:

我正在为一些开发实现一组 RESTful 服务,其中一个是身份验证服务

身份验证服务对两种身份进行身份验证:

  • 应用程序基于 AppKey 的身份验证,因此客户端必须注册密钥才能访问其余服务
  • 用户基于众所周知的凭据(用户+密码)的用户身份验证,因此人类和机器可以通过客户端应用程序使用这些 RESTful 服务。

这些 RESTful 服务无状态的

当客户端应用程序根据身份验证服务进行身份验证时,或者当人或机器使用凭据作为身份进行身份验证时,这两个操作都会生成 AppTokenUserToken 分别。

这些令牌是加盐哈希,因此对 RESTful 基础架构的后续请求将在不共享 AppKeys凭据 的情况下进行身份验证。

从完全无状态方法的角度来看,这些令牌不应存储在服务层的任何位置,而是以某种客户端状态(例如,Web 客户端将使用 HTTP cookies)。 这就是我当前的实现目前的工作方式

因为使用这些令牌重新验证每个请求并让服务层接收来自客户端的令牌,因此它可以比较来自客户端的令牌和检查它是否是一个有效的令牌在服务层重新生成它并与客户端拥有的进行比较太昂贵了,我已经实现了一个服务层 AppToken 和 UserToken,两者都有到期日期和所有者(为其创建令牌的应用程序或用户),以便检查来自客户端的令牌是否存在于令牌存储中。

客户端如何以交互方式取消身份验证? 只是删除客户端安全状态。如果是 Web 客户端,它会丢弃身份验证 cookie 并刷新页面,客户端检测到没有身份验证 cookie 并将用户重定向到登录页面。

从 RESTful 服务的角度来看,这是一种无状态的非身份验证:客户端不知道具有服务层伪身份验证状态的技巧这只是一个服务实现细节——一个性能优化——。

我不会列出无状态服务的优点,因为我绝对确定这种方法是可行的方法,但我发现了一个问题:无状态身份验证/未身份验证意味着客户端不会通知服务器他们关闭会话,因此安全存储以大量无用记录结束

如果服务客户端的会话时间有限(例如,1 小时、3 小时、一天...),这不是一个大问题,但是 如果用户必须经过身份验证会发生什么情况永远(8 个月,一年)?您如何区分什么是过期令牌?

有一些方法可以解决这种情况:

  1. 每当服务层收到请求时,它都会更新令牌到期日期,因此自动进程可能会丢弃那些已过期的令牌,从而定义令牌的任意到期时间(例如 24 小时)

  2. 妥协架构的无状态特性,让客户端通知服务层他们不想再进行身份验证,因此服务可以将关联的令牌丢弃到客户端会话但是等等……如果客户端关闭了 Web 客户端会发生什么?用户永远不会主动通知服务必须丢弃令牌……所以……僵尸令牌还在那里,所以自动化进程应该丢弃它们,但是。 .. 什么是僵尸令牌?我不喜欢这种方法)

  3. 完全无状态身份验证、无存储、按请求身份验证。

这是问题!您建议的方法是什么 - 即使不是 1.、2. 或 3. - 为什么?

感谢您的长时间阅读 - 我真诚地相信问题的结论将对任何人都非常有用 -!

【问题讨论】:

  • 你在服务层对token做了哪些检查,怎么这么贵?
  • @Henrik 令牌由一些用户信息+秘密盐+发出日期组成
  • 盐的秘密是谁的?每个服务是否共享盐的知识,还是仅共享身份验证服务?
  • @Henrik 它在业务层面实现。此机密由身份验证管理器处理。顺便说一句,也许我迷路了,但似乎这个细节与我的问题无关。
  • 盐用于增加熵,因此您不能在整个摘要/散列范围内使用彩虹表,但一次只能在单个散列 + 盐上使用。我不确定这就是你所追求的。因此,如果您的服务不知道盐,他们不知道如何重新创建哈希,那么您需要每个服务每次都与身份验证管理器对话,因为这就是知识所在。

标签: .net rest authentication .net-4.5 c#-5.0


【解决方案1】:

第三个

无状态身份验证,基于令牌。假设传输级加密。

  • [X]S<sub>S</sub> -- X 由 S 的公钥签名
  • [X|Y] -- X 和 Y 在同一个信封中
  • Y [M]S<sub>Y</sub> -&gt; S -- Y 向 S 发送签名消息 M。

目标: 客户 C 希望与服务 S 通话。

  1. 客户端 C 将其共享密钥或公钥 PK<sub>C</sub> 发送给服务 A 进行身份验证,C 知道服务 A 的端点和公钥 (PK<sub>A</sub>)。

  2. A [now + interval | user-id or PK<sub>C</sub>]S<sub>A</sub> -&gt; C

    解释:

    服务 A 在当前日期/时间上添加一个时间间隔作为过期时间。在要发送的缓冲区中现在是到期日期和用户 ID,PK<sub>C</sub>(假设您有一个有效的身份提供者)。

    [now + interval | user-id or PK<sub>C</sub>] = T

    A 签名;

    [T]S<sub>A</sub>

  3. 客户端 C 希望与后端服务 S 对话。

  4. C [[M|[T]S<sub>A</sub>]S<sub>C</sub> -&gt; S

    C 将消息 M 和它从 A 签署的令牌发送到服务 S。

  5. S 关心 C 确实发送了它并验证 C 的签名 S<sub>C</sub> 是从信封中读取的。

    S 验证令牌的签名S<sub>A</sub>。失败意味着请求被拒绝。

    S 验证令牌 [T]S<sub>A</sub>:用户 ID/PK<sub>C</sub> 正确且令牌日期 >= 现在。 Expired token 表示向客户端 C 发送“token expired”消息。如果 token 签名错误,则权限被拒绝。

    (可选;S授权C,题外话)

  6. S 执行工作并将[M<sub>2</sub>]S<sub>S</sub> 发送回客户端 C。

这不会有太多开销;验证签名是一个非常快的操作。

证书

问题“C# Sign Data with RSA using Bouncy Castle”显示了您如何签署和验证一段字符串,即您发送的消息。

您需要证书;如果你正在使用配置管理器(你应该这样做!;)),比如 puppet,那么你 create a certificate signing request (CSR) 然后 sign it using puppet

特别是在unauthentication上发布脚本

有一种叫做证书撤销请求的东西,它基本上是一个已被撤回且不可信任/使用的公钥的洗衣清单。将PK<sub>C</sub> 放在那里并广播撤销,它基本上通过要求客户端执行另一轮证书签名请求来起作用。

此外,如果您希望能够使特定令牌过期,请在创建令牌时将唯一 ID (UUID/GUID) 添加到令牌 T,并进行 令牌撤销列表,类似地在更改时广播,您可以在令牌 UUID 过期时清除它们。因此,如果接收到的 T 在其中,服务也会检查 令牌撤销列表

基于哈希的令牌

看看软件巨头在做什么。例如。 Amazon 的 REST 接口,使用共享密钥:

Amazon S3 REST API 使用基于键控 HMAC 的自定义 HTTP 方案 (Hash Message Authentication Code) 进行身份验证。进行身份验证 请求,您首先将请求的选定元素连接到 形成一个字符串。然后,您使用您的 AWS 秘密访问密钥来计算 该字符串的 HMAC。非正式地,我们将此过程称为“签署 请求”,我们将 HMAC 算法的输出称为“签名” 因为它模拟了真实签名的安全属性。 最后,将此签名添加为请求的参数,使用 本节中描述的语法。

Read more on Amazon's scheme.

上面的颠覆/攻击向量

  • 我最初描述的方案需要 SSL,它容易受到众多证书颁发机构 as well as a number of other things 的攻击。
  • 您很容易受到重放攻击,即中间人重新发送消息。如果您的 REST 接口是幂等的,那么您是安全的。如果您将服务器已知的加密 nonce 添加到请求中,您也是安全的。

【讨论】:

  • 感谢您的回答。让我们看看其他人是否有其他意见。完全无状态的身份验证是一个不错的选择。
  • 我对您的回答有疑问。服务如何验证令牌是否有效?我的意思是,因为好的,身份验证服务发出一个 token 并且 Web 客户端将它存储为一个令牌。下一个请求使用此令牌对应用程序或用户进行身份验证。由于令牌是哈希,因此您无法将其还原以了解其源纯文本。您如何在服务中验证此令牌?您是否认为从具有有效“AppKey”(客户端密码)的某个应用程序接收到的任何令牌都符合对请求进行身份验证的条件?
  • (continuation) ... 由于连接使用 HTTPS/SSL,并且应用程序使用唯一密钥(AppKey)向服务发出请求,因此密钥(appkey)+ 令牌对应该足够安全了解某些请求已通过身份验证。另一方面,在 Web 客户端中,令牌存储在 cookie 中,令牌过期时间等于 cookie 过期时间。如果 cookie 过期,token 也会过期。正如您在回答中所说,每次成功验证对服务层的请求后,cookie 过期时间都会增加。我错了吗?
  • 我们在谈论两个不同的事情。我在上面的回复中建议您引入公钥基础设施,因为它可以为您解决三个问题;身份验证、消息内容验证和加密。上例中 C 的公钥是身份验证服务查找调用者身份所需的唯一标识符。令牌由身份验证者签名,因此任何其他代理都难以伪造令牌何时到期或它的用途的有效性。 ...
  • 如果你使用哈希值,那么你需要在你的服务之间有一个共享的秘密,但是为了反转哈希值,看看它意味着什么,你需要某种查找;这就是你所说的慢(对于 S 收到的每个请求,对 A 或查找字典的额外调用;token -&gt; (expiry, uid);所以我解释了另一种方式,使用签名。
【解决方案2】:

选择的方法:完全无状态的身份验证和非身份验证

最后,我得到了一个结论和一个协议,以便切换到整个完全无状态的基于令牌的身份验证和非身份验证。

如何实现?

首先,这是您为应用程序提供基于令牌的无状态身份验证所需要的(但用户身份验证的工作方式相同,不包括此清单):

  • 应用程序注册系统。应用程序是对您的服务的访问。它是“您的应用程序访问网络上的某些服务(内联网、互联网、云......)。这是创建应用程序密钥(跳过此以进行用户身份验证)。
  • 服务器证书,因此客户端到服务的连接使用 HTTPS/SSL 进行加密。

这是验证应用程序的流程:

  1. 客户端向身份验证服务发送身份验证请求。此请求必须包含应用程序密钥 (AppKey)

  2. 身份验证服务接收到先前发送的请求。

  3. 现在身份验证服务会创建一个应用程序令牌 (AppToken),它是必要信息的自描述串联,用于跟踪具体的经过身份验证的客户端到依赖于身份验证服务的服务。

  4. AppToken 是一个复合字符串(这个组合可以是一个使用 JSON 序列化的对象):

    • 应用程序哈希(*a SHA - 或其他 - 是连接一些应用程序信息的结果。此信息将是服务机密 + 到期日期(它是令牌本身的一部分). 为什么要过期?。想象一下中间的人或其他人可以破坏安全并修改令牌的过期时间?当加密的令牌被解密以验证请求时,再次哈希的结果过期date + AppKey 将不再产生相同的哈希,因此令牌无效。
    • 发布日期。创建令牌时的当前 UTC 日期+时间。
    • 有效期。令牌将不再有效的 UTC DateT+Time。
  5. 身份验证服务加密第 4 步结果(JSON 序列化对象)。 **使用 AppKey 作为对称密码的密钥或密码。就我而言,我将为此使用 Rijndael。

  6. 后续请求将包含此令牌,以避免发送纯文本凭据。这些请求也将始终包含 AppKey,因此身份验证服务将能够识别正在尝试对请求进行身份验证的应用程序。

  7. 一段时间后,令牌过期或无效,客户端请求新的 AppToken。或者客户端被用户关闭并且没有可以保存安全令牌的持久存储,因此下一个客户端会话将在需要时请求新的。


关于这种身份验证方法的.NET实现的一些提示和细节:

  • 我使用System.Security.Cryptography.RijndaelManaged 类进行对称加密。 AppKey 和 AppToken(在基于令牌的用户身份验证的情况下,几乎是相同的解决方案)都是使用 RijndaelManaged 类生成的。

  • 加密文本被转换为十六进制字符串。这与身份验证响应一起发送。在我们的例子中(一个 RESTFul API),代表 AppToken 的 HEX 字符串将作为响应头发送。每当请求包含此 HEX 字符串时,身份验证过程会将其重新转换为原始加密文本,然后将其解密以评估令牌是否有效。


感谢 Henrik 的努力。我在您自己的答案中采用了一些概念,并将它们与我自己的结论混合在一起。

【讨论】:

  • 这不是我写的;)。我将编写一些代码并将其包含在内,因为这是明确的。
  • @Henrik 这是根据您的回答和我自己的调查得出的结论摘要。我相信您的答案是一个很好的答案,但正如我在一些评论中所说的那样,它太技术化了。不要以为任何人都会明白。我认为 Stackoverflow 应该提供直接、易于理解的答案。这就是为什么我试图用一个文本来总结所有内容:D
  • 是的,这很好,但是你已经改变了一些部分,这就是为什么我宁愿改进我自己的答案...... :)
  • @Henrik 而且,为什么不呢?这不是为了创造比赛,而是为社区做出贡献并分享我们的知识!!
  • 我的意思是,您更改了一些破坏协议(或更改协议)的重要部分。但我认为我们现在不会在 cmets 方面取得进一步进展。
猜你喜欢
  • 2015-07-09
  • 1970-01-01
  • 2014-01-08
  • 2019-01-25
  • 2022-06-23
  • 1970-01-01
  • 2011-04-09
  • 2013-10-07
  • 2016-07-09
相关资源
最近更新 更多