zengzzzzz the more you know, the less you know

JWT SHA256

2023-03-15

最近在对接外部服务时,遇到JWT相关的一些问题,这篇文章做一下关于JWT的一些总结。

摘要

本文内容主要有俩点:1 说明JWT采用HS256生成token的基本原理 ; 2 说明github 主流JWT生成库在采用HS256情况下生成token不一致的原因;

问题说明

在某次对接外部平台的API接口时,对方说明需要采用JWT HS256获取授权token,但是在生成token的过程中,自己编码实现及采用第三方包生成,以及不同第三方包生成的token之间存在差异,部分可以调通对方接口,部分则不可以,遂引发对该问题的探究。

JWT简介

JSON Web Token 是一种开放标准 (涉及到的RFC: RF7515 RF7519 RF7797 RF8725 )定义了一种用于可以在各方之间安全传输信息的Json对象。该对象可以通过 HMAC 算法或者使用 RSA ECDSA的公钥、私钥进行签名,则该信息是可以进行验证和信任的。

使用场景

授权

授权是最常见的场景,用户登录后,每个后续请求都将包含JWT,允许用户访问该令牌允许的路由、服务器和资源。通常使用在Bearer 模式的 Authorization header中,格式如下:

Authorization: Bearer <token>
信息交换

JWT可以进行安全传输信息,JWT可以签名,可以使用公钥、私钥对确定发送者的身份,验证内容是否被篡改。

JWT结构

在紧凑的形式中,JWT 由三部分组成,采用点 (.) 进行分隔,其通常结构如下:

xxxxx.yyyyyy.zzzzzz

header 通常由两部分组成:令牌类型 JWT 及所使用的签名算法 eg: SHA256 , 该json被Base64Url编码作为JWT的第一部分,格式如下:

{
  "alg":"HS256",
  "typ":"JWT"
}
payload

payload 指有效负载,其中包含关于实体和附加数据的陈述作用的声明,分为三种类型,包含注册声明、公共声明、私人声明等。

注册声明

预定义的声明,并非强制性,但建议使用,类似: iss (发行者)、exp(到期时间)、sub(主题)

公共声明

可以由使用JWT的人随意定义,但是为了避免冲突,应该在 Iana json web token 令牌注册表中定义

私人声明

在同意使用它们各方之间共享信息而创建的自定义声明

格式如下:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
signature

创建signatire需要 encoded header 、encoded payload 、 secret key 、algorithm 对其进行签名, HMAC SHA256 签名算法如下:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
putting all together

输出是由点分割的三个 Base64-URL 字符串,即为JWT格式,格式如下:

 base64UrlEncode(header).base64UrlEncode(payload).signature

比较研究

RFC 文档比较

在JWT中主要涉及到的RFC文档包括: RF7515 RF7519 RF7797 RF8725

RF7515与RFC7797的比较

RFC7515 RFC7797 都是关于 JWS (JSON web signature)的相关规范,但其涉及到JWS的不同方面并服务于不同的目的。

名称 简述 主要变更点说明
RFC7515 定义JWS的语法与处理,指定了JWS的格式及并描述了如何对JWS内容进行数据签名或者验证,提供了在应用程序中使用JWS的基本框架  
RFC7797 是RFC7515的一项补充规范,向JWS 引入分离内容的功能,允许将JWS payload 与 JWS 本身分开存储和传输,为处理 payload 比较大或者分离的情况,不必对payload 进行编码,以改善空间和时间 在该RFC文档的实现下,将会在header中添加标注b64 以标明是否需要进行Base64url 转化,则在一些具体的实现或者对接文档中会见到并不会对payload进行base64url 转化,这种现象也是正常的。

RFC7519与RFC8725比较

RFC7519 RFC8725 都是关于 JWT (JSON Web Tokens)的相关规范, RF8725 是对JWT的最佳实践的一些规范。

名称 简述 主要变更点说明
RFC7519 定义了JWT的主要规范,在本文的 JWT结构部分便是按照RFC7519的  
RFC8725 提供了在不同上下文中安全使用JWT的最佳实践,提供了使用JWT的指南和建议,包含令牌格式、密码算法、令牌生命周期等主题。 使用HTTPS 传输JWT
限制JWT生命周期
使用强密码算法
避免在错误信息或者其他响应中泄漏时间信息

第三方模块比较

第三方模块比较

因为在第三方服务对接时,通常使用python,则搜索github,并在其中发现可用于直接调用的主流库有pyjwtpython-joseauthlib等,该处选择主要的pyjwt与authlib进行对比分析。

pyjwt

简介

python基于RFC7519实现的关于JWT解码编码的第三方模块 github

主要提供以下功能:

  1. 生成JWT

    使用PyJWT可以方便地生成JWT,该过程包括设置头部、载荷和签名。可以使用多种算法来生成签名,如HMAC、RSA和ECDSA。下面是一个使用HMAC-SHA256算法生成JWT的示例:

    import jwt
    payload = {'user_id': 1234567890, 'name': 'John Doe'}
    secret_key = 'secret'
    jwt_token = jwt.encode(payload, secret_key, algorithm='HS256')
    print(jwt_token)
    
  2. 解码JWT

    使用PyJWT可以轻松解码JWT,获取其头部和载荷信息。下面是一个解码JWT并获取信息的示例

    import jwt
    jwt_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJuYW1lIjoiSm9obiBEb2UifQ.pS6S...XqW8'
    secret_key = 'secret'
    decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
    print(decoded)
    
  3. 验证签名

    使用PyJWT可以验证JWT的签名是否有效。可以使用多种算法来验证签名,如HMAC、RSA和ECDSA。下面是一个验证JWT签名的示例

    import jwt
    jwt_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJuYW1lIjoiSm9obiBEb2UifQ.pS6S...XqW8'
    secret_key = 'secret'
    try:
        decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
        print("JWT signature is valid")
    except jwt.InvalidSignatureError:
        print("JWT signature is invalid")
    

authlib

简介

用于简化Web应用程序中身份验证和授权的实现。它提供了一系列工具和协议的实现,以便于开发人员在应用程序中轻松添加安全验证和授权功能,如OAuth、OpenID Connec、JWT等。

Authlib的主要功能包括:

  1. OAuth支持:Authlib提供了OAuth 1.0、OAuth 2.0协议的实现,包括授权、访问令牌管理等功能。OAuth是一种流行的授权协议,用于允许第三方应用程序在用户授权的情况下访问他们的资源。
  2. OpenID Connect支持:Authlib提供了OpenID Connect协议的实现,它建立在OAuth 2.0协议之上,为身份验证提供了一个标准化的框架。开发人员可以使用Authlib来验证OpenID Connect提供者的令牌、获取用户信息等。
  3. JWT支持:Authlib提供了对JSON Web Tokens(JWTs)的支持,用于处理安全令牌的编码、解码和验证。JWT是一种开放标准,用于在不同实体之间安全地传输信息,比如在客户端和服务器之间。
  4. 第三方身份验证支持:Authlib支持多种第三方身份验证提供商,如Google、Facebook、Twitter等。开发人员可以使用Authlib来实现与这些身份验证提供商的身份验证和授权。
  5. 多种框架支持:Authlib支持多种Web框架,如Flask、Django、FastAPI等。开发人员可以使用Authlib来简化身份验证和授权的实现,并且不需要编写大量的重复代码。

JWT编码和解码示例:

import time
from authlib.jose import jwt

# 编码JWT
payload = {'sub': '1234567890', 'name': 'John Doe', 'iat': int(time.time())}
secret_key = 'your-secret-key'
encoded_jwt = jwt.encode({'alg': 'HS256', 'typ': 'JWT'}, payload, secret_key)

# 解码JWT
decoded_jwt = jwt.decode(encoded_jwt, secret_key, algorithms=['HS256'])

pyjwt authlib 比较

名称 区别
pyjwt pyjwt主要关注JWT,提供了编码、解码和验证JWT的基本功能
pyjwt相对来说比较小巧,代码量少,功能相对简单,可更容易实现定制化的解决方案
authlib authlib提供了更全面的OAuth 1.0a/2.0和OpenID Connect协议支持,同时也提供了JWT编码和解码的功能
功能更多,配置更加复杂,定制化开发难度较pyjwt高

总结

综上,在实际使用过程中,我们在使用第三方库或者自己进行JWT对接时,要了解对方所使用的具体的RFC协议,而不仅仅是指定JWT及加密方式就可以的。在不同的RFC协议版本中,对同种方式的实现是存在差别的的,而这种差别可能会导致我们不能及时与对方进行对接,尤其是在使用第三方库时,要了解第三方库中关于JWT的具体实现时所遵循的RFC协议,以避免不必要的问题。

参考文档

https://jwt.io/

https://www.iana.org/assignments/jwt/jwt.xhtml

https://www.rfc-editor.org/rfc/rfc6750

https://www.rfc-editor.org/rfc/rfc1945

https://en.wikipedia.org/wiki/HMAC

https://en.wikipedia.org/wiki/RSA_(cryptosystem)

https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm

https://www.rfc-editor.org/rfc/rfc7515

https://www.rfc-editor.org/rfc/rfc7519

https://www.rfc-editor.org/rfc/rfc7797

https://www.rfc-editor.org/rfc/rfc8725

https://base64.guru/standards/base64url

https://github.com/jpadilla/pyjwt/


下一篇 cpu cache test

Comments

Content