JWT安全
JWT
JWT定义
- JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT作用
- 授权:一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。它的开销很小并且可以在不同的域中使用。如:单点登录。
- 信息交换:在各方之间安全地传输信息。JWT可进行签名(如使用公钥/私钥对),因此可确保发件人。由于签名是使用标头和有效负载计算的,因此还可验证内容是否被篡改。
JWT区别
传统Session
- http协议本身是一种无状态的协议,如果用户向服务器提供了用户名和密码来进行用户认证,下次请求时,用户还要再一次进行用户认证才行。因为根据http协议,服务器并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样应用就能识别请求来自哪个用户。
- 这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
- 举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
- 一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
- 另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
与Session的区别
- 两者的主要目的都是存储用户信息,但是session将用户信息存储再服务器端,而JWT则是在客户端。JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
JWT认证
认证流程
- 前端通过Web表单将自己的用户名和密码发送到后端的接口。该过程一般是HTTP的POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
- 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。
- 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage(浏览器本地缓存)或sessionStorage(session缓存)上,退出登录时前端删除保存的JWT即可。
- 前端在每次请求时将JWT放入HTTP的Header中的Authorization位。(解决XSS和XSRF问题)HEADER
- 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确﹔检查Token是否过期;检查Token的接收方是否是自己(可选)
- 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。
JWT优点
- 简洁(Compact):可以通过URL,POST参数或者在HTTP header发送,数据量小,传输速度也很快;
- 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库;
- Token是以JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上任何web形式都支持。
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
JWT的结构
令牌组成:
- 标头(Header)
- 有效载荷(Payload)
- 签名(Signature)
标头Header
- Header是一个JSON对象,描述JWT的元数据
1
2
3
4{
"alg": "HS256",
"typ": "JWT"
} - 上面代码中,
alg
属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256); typ
属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
。- 最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。
有效载荷Payload
- Payload是一个JSON对象,用来存放实际需要传递的数据。
JWT规定了7个官方字段:
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号1
2
3
4
5{
"sub": "1234567890",
"name": "John Doe",
"admin": true
} - 注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
- 这个 JSON 对象也要使用 Base64URL 算法转成字符串。
签名Signature
- Signature是对前面两部分的前面,防止数据篡改。
- 首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
1
2
3
4HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret) - 算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(
.
)分隔,就可以返回给用户。
Base64URL
- Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。
- JWT 作为一个令牌(token),有些场合可能会放到 URL(比如
api.example.com/?token=xxx
)。Base64 有三个字符+
、/
和=
,在 URL 里面有特殊含义,所以要被替换掉:=
被省略、+
替换成-
,/
替换成_
。这就是 Base64URL 算法。
Refresh token
- JWT使用
refresh token
去刷新access token
而无需再次身份验证。refresh token
的存活时间较长而access token
的存活时间较短。 - 登陆时会获取
access token
,refresh token
: - 服务器中可能存在:未校验
access token
和refresh token
是否属于同一个用户,导致A用户可使用自己的refresh token
去刷新B用户的access token
实例WebGoat-JWTtokens
lessen-03
- 这一关直接给出JWT令牌,我们需要从中找出username,并填入文本框
- 使用工具网站https://jwt.io/
- 可以看见username就在当中,内容是user
- Congratulations. You have successfully completed the assignment.
- 祝贺你。你已经成功地完成了任务。
lessen-05
- 这一关是使用管理员账户,重置投票
- 使用tom账户,点击垃圾桶图标(Reset votes),然后用抓包工具,修改access_token值
- 原本的access_token值为
1
access_token=eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2OTk1NDA4MzYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.GEiBddurt9oevq8htQTKWxXGqSNAZ89h8J1AoZn0lPT2ZLArIJ-3rZv_-1CsajQxw21MgNNvzTDab_IX_HPaRQ;
- 将alg值改为none,目的是不写签名,因为改了内容签名就不一样了
- 将admin值改为true,目的是将其标记为管理员
- 这个网站改不了,建议用burp的编码器改
- 其中符号都会被忽略,所有base64编码后的
=
都需要去掉,然后将签名也删了
JWT安全防护
- 修复算法,不允许客户端切换算法。
- 使用对称密钥对令牌进行签名时,确保使用适当的密钥长度。
- 确保添加到令牌的声明不包含个人信息。如果需要添加更多信息,也可以选择加密令牌。向项目添加足够的测试用例以验证无效令牌实际上不起作用。
评论