JWT介绍

1,677 阅读6分钟

jwt基础

JSON WEB TOKEN 是最新出现的一个跨域认证方案,主要是采用签名认证的方式来传递信息。经常使用的算法有HMAC算法,或者是公私钥加密(RSA)来进行。

JWT常用的场景:认证、信息交换

  • 认证:在用户访问服务时,头部增加jwt来作为认证方式,与常见的cookie认证,sessions认证类似,不同的是,jwt认证可以方便的用在不同的域下面,并且对于服务的拓展也是很方便的,只要在请求头中增加header头即可
  • 信息交换:由于jwt采用的是非对称加密方式,发送者可以用自己的私钥进行jwt的内容签名,接收者可以判断出是谁发出的信息

JWT的构成

JWT由三个部分组成:头部、载荷、签名

  • 头部

是用来描述jwt的基本信息的,实例如下:

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

其中描述的就是一个jwt,并且使用的算法是HS256来做的签名,对他进行base64编码即为JWT的头部信息:

 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
  • 载荷

本身是一个json对象,里面保存这要传递的信息:

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "jrocket@example.com",
    "from_user": "B",
    "target_user": "A"
}

这里面的前五个字段都是由JWT的标准所定义的。

  • iss: 该JWT的签发者
  • sub: 该JWT所面向的用户
  • aud: 接收该JWT的一方
  • exp(expires): 什么时候过期,这里是一个Unix时间戳
  • iat(issued at): 在什么时候签发的

将上面的JSON对象进行[base64编码]可以得到下面的字符即为jwt的载荷:

eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
  • 签名

将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9

最后,将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret)。如果我们用mystar作为密钥的话,那么就可以得到我们加密后的内容

rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
  • 生成

最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
  • 验证

可以使用jwt.io Debugger来进行验证:

使用mystar进行验证:

JWT使用过程

在一个用户登入成功后,一个json web token会返回给他,并且存储在他的本地中,包括cookie或者是localstorage下面。

此后每次通信都需要带上这个JWT,一般情况下是放在http请求头中,如:

Authorization: Bearer <token>

这样,每次的请求服务器都会验证签名是否正确,正确的话确认是以认证用户发出的,完成后面的操作

这样不是使用的cookie认证,可以完美的解决跨域请求的问题

但是由于本身jwt不是为数据加密来准备的,所以敏感数据不应该采用jwt的方式进行传输

JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

JWT本身并没有防止重放攻击的设计,也就是说一旦一个请求的JWT泄露了,攻击者可以使用这个来重新发起操作请求。

JWT攻击方式

JWT的攻击方式主要有:XSS、sql注入、暴力碰撞、重放攻击、信息泄露、无效签名等

  • XSS

XSS并不是针对JWT进行的攻击,而是获取JWT内容的,XSS可以获取cookie或者localstage中的内容,一旦获取到了就可以使用该jwt发起操作请求

  • 重放攻击

和xss攻击类似的攻击,主要还是为了获取jwt重放发起操作请求

  • sql注入

这个就需要说下jwt的另一个可选字段:kid ,代表着密钥序号,是会从数据库中查询该序号用到的是那个密钥。具体的用法如下:


{
    "alg": "HS256",
    "typ": "JWT",
    "kid": "1"         //使用密钥1验证token
}

这样一旦有了操作数据的行为即可能导致sql注入攻击

  • 暴力碰撞

由于签名使用的是密钥,只要对密钥空间遍历就有可能碰撞出正确的密钥,这样就能伪造任意用户进行签名操作了

工具可以参见:github.com/ticarpi/jwt…

  • 信息泄露

这个问题主要是jwt不是为了信息保密来做的,一旦在jwt中传输敏感信息则直接可以泄露信息,base64不是加密方式

  • 无效签名

这个有三种攻击方式:签名为空、算法混淆、无效签名,这几个也是jwt特有的攻击方式

  1. 签名为空

JWT支持将算法设定为“None”。如果"alg"字段设为"None",那么签名会被置空,这样任何token都是有效的。

例如前面的案例:

 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

将alg字段置为空,则生成为:

ewogICAgInR5cCI6ICJKV1QiLAogICAgImFsZyI6ICJIUzI1NiIKfQ==.eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9.

若是服务器依然返回正确,说明可以使用签名为空伪造任意用户的请求了

  1. 算法混淆

JWT中最常用的两种算法为HMAC和RSA。

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,它是一种对称加密算法,使用相同的密钥对传输信息进行加解密

RSA则是一种非对称加密算法,使用私钥加密明文,公钥解密密文。

攻击方法主要是将web应用中采用的rsa强制转化成对称密钥加密方式,将RS256改为对称的HS256,并且非对称的公钥是可以获取到的,这样重新签名发给服务器端,若是成功,则说明存在问题

  1. 无效的签名

这个主要就是服务器根本没有验证签名信息,任意签名私钥签名的数据均能过,导致问题存在