REST API 中基于令牌的身份验证

Token based authentication in REST APIs

我正在尝试实施基于令牌的身份验证方法:

  1. 每次成功登录都会创建新令牌。

  2. 如果用户选择 "keep me logged in" 或用户使用的是移动设备,令牌将永久保存在 Redis 数据库中,没有过期日期。否则,令牌将在 20 分钟后过期。

  3. 一旦用户通过身份验证,将从我的 Redis 数据库中的每个后续请求检查令牌。

我想知道如何识别设备。对于移动设备,我可以使用设备标识符。但是如何识别浏览器呢?

示例:用户使用 Chrome 登录并选择 "keep me logged in"。生成令牌并在 Redis 中使用浏览器名称持久保存。如果用户从 Firefox 登录,则将令牌和 "Firefox" 保存在数据库中。我将令牌保存在 Redis 中,而令牌是在身份验证成功时创建的。只保留令牌和使用令牌的浏览器是否可以?或者我还需要保留 IP 吗?

附加问题:如何避免攻击者从 cookie 中窃取令牌?

一旦服务器收到来自客户端的请求,它就会包含用户代理。此属性将帮助我们识别客户端。

请参考这个link:How do I detect what browser is used to access my site?

token-based 身份验证的工作原理

简而言之,基于令牌的身份验证方案遵循以下步骤:

  1. 客户端将其凭据(用户名和密码)发送到服务器。
  2. 服务器验证凭据并生成令牌。
  3. 服务器将先前生成的令牌连同用户标识符和到期日期一起存储在某个存储器中。
  4. 服务器将生成的令牌发送给客户端。
  5. 在每个请求中,客户端都会向服务器发送令牌。
  6. 服务器在每个请求中从传入请求中提取令牌。服务器通过token查询用户的详细信息,进行认证授权。
    1. 如果令牌有效,服务器接受请求。
    2. 如果令牌无效,服务器拒绝请求。
  7. 服务器可以提供端点来刷新令牌。

如何将凭据发送到服务器

在 REST 应用程序中,从客户端到服务器的每个请求都必须包含服务器可以理解的所有必要信息。有了它,您不依赖于存储在服务器上的任何 session 上下文,也不会破坏 stateless constraint of the REST architecture defined by Roy T. Fielding in his dissertation:

5.1.3 Stateless

[...] each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. [...]

访问需要身份验证的受保护资源时,每个请求都必须包含所有必要的数据才能正确authenticated/authorized。这意味着将对每个请求执行身份验证

看看 RFC 7235 关于新身份验证方案注意事项的引述:

5.1.2. Considerations for New Authentication Schemes

There are certain aspects of the HTTP Authentication Framework that put constraints on how new authentication schemes can work:

  • HTTP authentication is presumed to be stateless: all of the information necessary to authenticate a request MUST be provided in the request, rather than be dependent on the server remembering prior requests. [...]

并且身份验证数据(凭据)应属于标准 HTTP Authorization header. From the RFC 7235:

4.2. Authorization

The Authorization header field allows a user agent to authenticate itself with an origin server -- usually, but not necessarily, after receiving a 401 (Unauthorized) response. Its value consists of credentials containing the authentication information of the user agent for the realm of the resource being requested.

Authorization = credentials

[...]

请注意,此 HTTP header 的名称很不幸,因为它携带 authentication 数据而不是 authorization。无论如何,这是发送 凭据 .

的标准 header

执行基于令牌的身份验证时,令牌是您的凭据。在这种方法中,您的硬凭证(用户名和密码)被交换为在每个请求中发送的令牌。

令牌是什么样的

身份验证令牌是由服务器生成的用于识别用户的一段数据。基本上,标记可以是 opaque(除了值本身不显示任何细节,如 随机字符串 )或者可以是 self-contained(如JSON Web Token):

  • 随机字符串:令牌可以通过生成随机字符串并将其保存到具有到期日期和关联的用户标识符的数据库来颁发它。

  • JSON Web 令牌 (JWT):由 RFC 7519, it's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server. You won't need to persist JWT tokens if you don't need to track them. Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To keep the track of JWT tokens, instead of persisting the whole token, you could persist the token identifier (the jti claim) and some metadata (the user you issued the token for, the expiration date, etc) if you need. To find some great resources to work with JWT, have a look at http://jwt.io.

    定义

提示:始终考虑删除旧令牌,以防止您的数据库无限增长。

如何接受令牌

您应该从不接受过期的令牌或不是您的应用程序颁发的令牌。如果您使用的是 JWT,则必须检查令牌签名。

请注意,一旦您发行令牌并将其交给您的客户,您就无法控制客户将如何使用该令牌。 无控制认真.

如果需要,检查 User-Agent header field to tell which browser is being used to access your API. However, it's worth mention that HTTP headers can be easily spoofed and you should never trust your client. Browsers don't have unique identifier, but you can get a good level of fingerprinting 是一种常见的做法。

我不知道你的安全要求,但你总是可以在你的服务器中尝试以下操作来增强你的 API:

的安全性
  • 检查颁发令牌时用户使用的是哪个浏览器。如果浏览器在以下请求中不同,则拒绝令牌。
  • 在颁发令牌时获取客户端远程地址(即客户端IP地址),并使用第三方API查找客户端位置。如果以下请求来自其他国家的地址,例如,拒绝令牌。要通过 IP 地址查找位置,您可以尝试免费 APIs,例如 MaxMind GeoLite2 or IPInfoDB. Mind that hitting a third party API for each request your API receives is not a good idea and can cause a severe damage to the performance. But you can minimize the impact with a cache, by storing the client remote address and its location. There are a few cache engines available nowadays. To mention a few: Guava, Infinispan, Ehcache and Spring

通过网络发送敏感数据时,HTTPS 是您最好的朋友,它可以保护您的应用程序免受 man-in-the-middle attack.

顺便说一句,我有没有提到你永远不应该相信你的客户?