可以在 BEARER Authorization Header 中嵌入用户名吗?
Posted
技术标签:
【中文标题】可以在 BEARER Authorization Header 中嵌入用户名吗?【英文标题】:Is it okay to embed username in BEARER Authorization Header? 【发布时间】:2021-05-28 20:52:10 【问题描述】:我有一个使用 Web API 创建的 API 端点来创建安全令牌。因此用户在基本授权标头中传递用户名:密码。验证用户名/密码并返回 Json Web Token。
现在我的所有其他执行 GET 或 POST 的端点都将在 Authorization 标头中作为 BEARER 类型传入 JWT。我通常知道这是 BEARER + (jwt) 但它是否可以通过 BEARER + 用户名:(jwt)
我想传递用户名:(jwt) 的原因是,当我验证 jwt 时,我还想从 jwt 声明中提取用户名,并将其与 auth 标头中传递的用户名进行比较,作为额外检查。
这种方法听起来不错,还是我应该完全忘记检查用户名,因为它不会增加任何额外的安全性?
【问题讨论】:
正确理解:您将基本身份验证信息与 JWT 一起发送?此外,预定义的声明sub
用于包含用户名。编辑:查看标准化声明的 openid 连接声明定义openid.net/specs/openid-connect-core-1_0.html#StandardClaims
我在请求 JWT 时发送基本身份验证。收到 JWT 后,我想使用 Bearer Auth 来调用其他 API 端点,我只是问是否可以使用用户名+令牌对,而不是仅在该承载身份验证中发送令牌。至于声明,我在 GetToken 代码中定义声明时使用 ClaimTypes.Name。
好的,但我不明白您为什么要发送用户名 + 令牌,因为您可以/可以将其放在令牌内。
我在调用其他端点时在想,也许一个坏演员只有两个要调用的项目之一,也许他们有令牌但用户名无效,他们试图传递一个无效的用户名和一个正确的用户名令牌,我可以将用户名拉入令牌并将其与传入的用户名进行比较,作为验证他们是否应该拥有令牌的另一种方式。所以你是说我不需要做额外的检查吗? Joma 还说无法验证用户名。我的想法是可以根据令牌中的那个来验证它。
我还想避免只从令牌中提取用户名,然后对用户名进行数据库查找。这是我第一次使用 JWT,我想确保在完成登录过程后,仅仅拥有 Token 就足够了。这个概念让我有点紧张。所以请原谅我的质疑。
【参考方案1】:
没有什么可以阻止您在令牌之外添加用户名,但是 您可以根据需要向令牌添加任意数量的声明,并且可以包含用户名。可以通过 IHttpContextAccessor 从您的客户端应用程序或 Web API 检索这些声明。
在您的客户端/API 中,您可以执行以下操作来检索声明:
List<Claim> claims = _context.Request.HttpContext.User.Claims.ToList();
其中 _context 的类型为 IHttpContextAccessor。
这将获取编码令牌的 Subject 属性,这是一个 ClaimsIdentity,由创建令牌时添加的声明列表组成。
您可以在令牌之外添加用户名,但是在客户端/API 验证了不记名令牌后,您需要的所有内容都可以更安全地存储并从令牌本身中检索。
此外,您可以在访问客户端/API 中的其他类时使用声明添加额外授权。
所有其他声明类型(如 sub、iat 等)在您的客户端/API 端经过身份验证后都是令牌中的属性。在创建令牌期间您需要做的是使用 SecurityTokenDescriptor 来创建令牌描述符,如下所示(缩写):
SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
Subject = new ClaimsIdentity(new[]
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Sub, username)),
Expires = DateTime.Now.AddMinutes(expiration_in_minutes),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
;
您需要确保命名空间在您的源代码中:
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
当我运行它来生成令牌,然后在 get 中解码 https://jwt.io/ 中的令牌时:
"unique_name": "usernam@xxxx.xxx",
"sub": "usernam@xxxx.xxx",
"nbf": 1622285741,
"exp": 1622287503,
"iat": 1622285741
使用 JwtRegisteredClaimNames.Sub 添加声明会在令牌中公开 sub。
试试看它是否有效。
【讨论】:
Andrew:我确实按照您的方式创建了我的 SecurityTokenDescriptor。但是有人告诉我 ClaimTypes.Name 不正确,但我看到您也将其作为名称。此外,一旦令牌从客户端发送回服务器,我从声明中检索我的用户名也没有问题。 我刚刚在我的本地开发环境中对此进行了一些实验,并且我已经设法让 Sub 出现在令牌中,我将编辑我的答案并希望有所帮助。【参考方案2】:您可以在 sub 声明中发送用户名/用户 ID。
令牌内的声明已签名。 username/userid(sub) 已签名,无需验证 username/userid 是否 ok/valid。
在您的情况下,BEARER 用户名:令牌
用户名(未签名,可更改,无法验证):
令牌(声明/标题/签名 - 所有内容均已签名/验证)>
令牌外的用户名/用户 ID 不会增加额外的安全性。
System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub 以避免在声明名称中写入“sub”。此命名空间位于 System.IdentityModel.Tokens.Jwt nuget 包中。
【讨论】:
Joma:我知道用户名是可以更改的,这就是为什么我会根据令牌中的签名用户名进行验证,作为额外检查令牌的传递者是一个好的参与者。您是否认为带有正常 jwt 验证的令牌 ALONE 足够安全?在我看来,jwt 验证只是确保令牌从发出之日起直到将其传递回服务器为止都没有改变。 另一个问题是我在定义我的 SecurityTokenDescriptor 时使用 System.Security.Claims.ClaimTypes 并且 SUB 不是可用的选择之一。我见过的所有示例都在代码中使用 ClaimType.Name 作为用户名。 ClaimType.Name 是否有可能被翻译或映射到 SUB?还是我需要在代码中硬编码“SUB”? 顺便说一句,检查传递的用户名与令牌嵌入的用户名的整个想法来自这篇文章。 c-sharpcorner.com/article/… 在第 3 步中。 攻击者发送所需的用户名和令牌。令牌内容不能通过签名保护更改,但用户名很容易更改。 为了避免这个问题(在另一个浏览器/机器中复制令牌),在令牌请求阶段您可以向服务器发送一个随机密钥,该密钥与 JwtToken-User 相关联并存储在服务器数据库中,此 random_key 也存储在您的客户端中,并使用客户端/浏览器指纹(deviceId)加密,以避免在另一台机器中复制。服务器的每个请求都会在客户端返回 Encrypt(jsonResult, random_key) 我们需要解密(encryptedResult, random_key)。以上是关于可以在 BEARER Authorization Header 中嵌入用户名吗?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我应该在 `Authorization: Bearer` 标头中发送令牌?
在 Ocelot API Gateway 中的特定路由中添加 Authorization Header(Authorization: Bearer access_token)
如何使用 nodejs 设置 Authorization Bearer 标头
登录操作后在 Guzzle HTTP 客户端中设置 Authorization Bearer 标头 - Laravel
NextAuth jwt 作为 header.Authorization Bearer Token Next Js
为啥在 React 中,我的 axios API 调用有 Authorization Header,其中包含 Bearer <token> 但未被授权并给出 401 错误