REST Web服务身份验证令牌实现

21

我正在使用C#实现一个REST Web服务,该服务将作为云服务托管在Azure上。由于它是REST服务,因此没有Cookie或会话状态。

Web服务只能通过HTTPS访问(由StartSSL.com提供的证书)。

用户成功登录到服务后,将获得安全令牌。该令牌将在未来通信中提供身份验证。

该令牌将包含客户端的时间戳、用户ID和IP地址。

所有通信都仅在HTTPS上发生,所以我不担心令牌被截获并用于重播攻击;令牌将具有过期时间。

由于这是一个面向公众的服务,我很担心有人注册服务,登录后修改接收到的令牌以访问其他用户帐户。

我想知道如何最好地保护令牌内容,并验证它是否已被篡改。

我计划执行以下步骤来保护令牌:

客户端成功登录到服务后,服务将执行以下操作:

  1. 生成随机值,并使用SHA256哈希1000次。
  2. 从私钥+散列随机值中生成一次性会话密钥。
  3. 使用SHA256 1000次对会话密钥进行哈希,然后用它来加密令牌
  4. 使用私钥使用RSA签名加密的令牌。
  5. 将加密的令牌+签名+散列随机值以未加密的JSON包发送给客户端。

当客户端调用服务时,它会向服务发送未加密的JSON包中的加密令牌和签名。服务将会:

  1. 从私钥+散列随机值中重新创建会话密钥
  2. 使用私钥验证签名
  3. 使用散列会话密钥解密令牌
  4. 检查令牌是否已过期
  5. 继续请求的操作...

我对加密一无所知,所以我有一些问题:

  1. 这是否足够或者过于复杂了?
  • 我了解到为了检测篡改,我应该在令牌中包含HMAC。 因为我正在使用私钥签名,所以我仍然需要HMAC吗?
  • 答:是的,即使使用私钥签名,为了检测篡改,您仍应该包括HMAC。
  • 我应该使用Rijndael而不是RSA吗?
  • 答:这取决于您的具体需求。如果您需要对称加密,则可以使用Rijndael,而非非对称加密RSA。
  • 如果Rijndael更受欢迎,那么生成的IV是否在解密时必需的? 例如,我可以丢弃它,还是需要将其与加密令牌一起发送? 例如:Encrypted Token + HMAC + IV + hashed random value。
  • 答:是的,您需要IV来解密Rijndael加密的令牌。因此,您不能丢弃它,需要将其与加密的令牌一起发送。

    由于所有通信都通过HTTPS进行,因此未加密的JSON数据包实际上直到到达客户端之前才会真正加密。

    此外,我可能希望稍后在PHP中重新实现服务,因此所有内容都需要在PHP中可行。

    感谢您的帮助

    1个回答

    26
    您过于纠结令牌。 实际上,最好的令牌安全性依赖于随机性,或者更准确地说是不可预测性。 最好的令牌是完全随机的。 您是正确的,一个问题是用户将修改他/她的令牌并使用它来访问其他人的帐户。 这是一种常见的攻击,称为“会话劫持”。 当令牌在服务器端随机生成并过期时,这种攻击几乎不可能。 使用用户的信息(如IP和/或时间戳)是不好的做法,因为它提高了可预测性。 我曾经在大学里进行一次攻击,成功猜出基于服务器微秒级时间戳的活动令牌。 应用程序的作者认为微秒会足够快地变化以至于不可预测,但事实并非如此。
    您应该注意,当用户在代理服务器后面时,代理有时会以明文方式查看其SSL请求(出于安全原因,许多代理将执行深度数据包检查)。 出于这个原因,您需要使会话过期。 如果您没有这样做,您的用户将容易受到此类攻击的攻击,还可能受到XSS和CSRF的攻击。
    RSA或Rijndael应该足够了,只要密钥长度合理。 此外,您应该将HMAC与令牌一起使用以防止篡改,即使您正在签名它。 从理论上讲,这是多余的,因为您正在使用私钥进行签名。 但是,HMAC经过了非常好的测试,并且您对签名机制的实现可能存在缺陷。 出于这个原因,最好使用HMAC。 您会惊讶地发现有多少“自定义”安全实现存在缺陷,从而导致他们妥协。
    您在安全方面似乎很精通。 保持良好的工作! 我们需要更多关注安全的开发人员。
    编辑:
    只要使用强大的对称秘密密钥(如AES、Blowfish等)对时间戳/用户ID进行加密,而且令牌包括与其一起的防篡改哈希(例如HMAC),则将时间戳/用户ID包含在令牌中被认为是安全的,该哈希已使用密钥加密。 哈希保证完整性,加密保证机密性。如果在加密过程中不包括HMAC(或其他哈希),那么用户可能会篡改加密令牌,并将其解密为有效内容。我曾经对一个服务器进行攻击,其中用户ID和时间戳被加密并用作令牌,但没有使用哈希。通过改变字符串中的一个随机字符,我能够将我的用户ID从58762更改为58531。虽然我无法选择“新”的用户ID,但我可以访问其他人的账户(这是在学术界进行的,作为课程的一部分)。
    另一种方法是使用完全随机的令牌值,并在服务器端将其映射到存储的用户ID/时间戳(保持在服务器端且因此不受客户端控制)。这需要更多的内存和处理能力,但更安全。这是您需要根据具体情况做出的决策。
    至于重复使用/派生IV和其他密钥,通常是可行的,前提是密钥只有短时间内有效。数学上很难破解它们,但并非不可能。如果您想走极端谨慎的路线(我通常会这样做),则应随机生成所有新密钥。

    谢谢。我决定使用Rijndael和HMAC,但仍在努力理解它。我可以使用IV从主密钥派生会话密钥吗?我还可以使用相同的会话密钥进行HMAC,还是需要从当前会话密钥派生新密钥?如果我需要一个新的HMAC密钥,我可以使用当前IV吗,还是也需要一个新的随机值?最后,您提到我不应该使用用户ID加密时间戳,那么我如何知道令牌已过期?因为我不能在数据库中存储会话记录。再次感谢。 - There is no spoon
    好问题。我会在答案中添加一些额外的信息。 - Freedom_Ben
    好的,我明白了。那么HMAC是从未加密的数据计算出来的,然后再与数据一起加密吗?我需要在服务器上存储HMAC以便以后进行比较,还是可以解密令牌,然后从现在未加密的数据计算出HMAC,并将其与使用数据加密的HMAC进行比较? - There is no spoon
    HMAC可以从未加密或加密的数据中计算出来,只要在用户控制下使用秘密密钥对HMAC进行加密即可。绝不能让用户访问未加密的HMAC,因为他们可以篡改数据(即使它已经加密)并相应地更新HMAC。您不需要将HMAC存储在服务器上,只要存储密钥并基于未加密的数据重新计算HMAC,并根据客户端提交的现在未加密的HMAC验证即可。这样讲清楚了吗? - Freedom_Ben
    1
    是的,这很有道理,您的答案清晰易懂。我想我可能会使用随机数令牌并维护一个会话表,而不是传递用户ID、时间戳等信息。虽然这违反了RESTful设计,但为了更好的安全性,这是一个小小的代价。 - There is no spoon

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