JWT
什么是JWT
JWT(JSON Web Token)是一种用于在各方之间作为JSON对象安全地传输信息的开放标准(RFC 7519)。该信息经过数字签名,因此是可验证和可信的。JWT 可以使用HMAC算法或使用RSA的公钥/私钥对进行签名
JWT的组成
- Header(头部):包含令牌的类型(JWT)和使用的签名算法(如HMAC SHA256或RSA)。
- Payload(负载):包含声明(claims)。声明是关于实体(通常是用户)和其他数据的断言。声明有三种类型:
- Registered claims(注册声明):这些是预定义的声明,如
iss
(签发者)、exp
(过期时间)、sub
(主题)、aud
(受众)等。它们不是强制的,但建议使用。- Public claims(公共声明):这些是可以在JWT中使用的自定义声明。为了避免冲突,应在IANA JSON Web Token Registry中定义或使用具有防冲突前缀的命名空间。
- Private claims(私有声明):这些是双方之间使用的自定义声明,通常是特定于应用程序的。
- Signature(签名):用于验证消息在传递过程中是否未被篡改。首先,您需要将编码后的头部和负载进行签名,然后使用指定的签名算法创建签名。签名的过程如下:
- 使用 Base64 URL 编码对 Header 和 Payload 进行编码。
- 使用编码后的 Header、编码后的 Payload 和一个密钥(对于 HMAC 算法)或私钥(对于 RSA)创建签名。
[HFCTF2020]EasyLogin
正常进行注册登录,发现有命令框,尝试了各种读取方式都没用,抓包发现存在jwt
再查看一下源码,看有没有什么提示
发现了关于flag的提示
解释一下这串代码的大致意思:
- AJAX 请求: 这段代码使用
$.get
向/api/flag
端点发起一个 AJAX GET 请求。- 成功回调: 如果请求成功,
done
方法会被调用,并传入返回的数据。
- 使用解构赋值从返回的数据中提取
flag
属性。- 将 ID 为
username
的输入元素的值设置为这个flag
。- 失败回调: 如果请求失败,
fail
方法会被调用。
xhr
: XMLHttpRequest 对象。xhr.responseJSON.message
: 从响应 JSON 中提取的错误信息,并在警告框中显示。
猜测flag应该就在/api/flag里面,提示了koa框架;
koa-static是一个处理静态文件资源的中间件,从大佬哪里借鉴一个koa的框架结构
controllers是项目控制器存放目录,访问来查看源码
结合前面提到的flag所在的目录,访问api目录
/controllers/api.js
发现了返回flag的条件
'GET /api/flag': async (ctx, next) => {if(ctx.session.username !== 'admin'){throw new APIError('permission error', 'permission denied');}const flag = fs.readFileSync('/flag').toString();ctx.rest({flag});await next();},
简单来说就是判断username是否为admin,如果是admin的话就读取'/flag'下的内容以字符串的形式存储在flag中
想要username为admin,就只有通过JWT伪造(因为注册为admin会报错)
那么解题思路也就明确了,通过jwt将username伪造为admin,来读取flag并返回
先解析返回的jwt,签名是HS256
伪造方法,把"alg"值更改为"none",这样就不执行签名验证,将secretid改为[]代表一个空值
修改之后进行base64编码(去掉换行符和编码后的"="),把header和payload拼接,因为签名改为了none所以签名认证就为空
进入/api/flag,抓包,修改一下sses:aok和sses:aok.sig,修改为伪造jwt后返回的对应的值
就完成了jwt的伪造,获取到flag
总结:这个周从复现开始就全是token的题,前面也接触过session,一开始只是草草了解token和session类似,但是现在把两个做了明显的区别对比
Token
客户端存储:Token是在客户端存储用户身份验证和状态信息的一种机制。通常,服务器会在用户登录成功后生成一个令牌(Token),并将其发送给客户端。客户端通常会将Token存储在本地,比如LocalStorage、SessionStorage或者Cookies中。
状态管理:Token同样用于跟踪用户的身份验证状态,但所有的状态信息都被编码到Token中。服务器在接收到包含Token的HTTP请求时,会解码Token并验证用户的身份。
无状态性:Token通常被设计为无状态的,这意味着服务器不需要在内存或者数据库中存储任何关于用户会话的信息。这使得系统更容易水平扩展,并提高了性能。
灵活性:Token可以在不同的服务之间轻松传递,因为所有的用户信息都被编码在Token中。这使得Token特别适合于微服务架构和分布式系统。
安全性:Token通常使用加密算法进行签名,以确保其完整性和真实性。另外,Token还可以包含过期时间和范围限制等信息,以增强安全性。
Session
服务器端存储:Session是在服务器端存储用户的身份验证和状态信息的一种机制。通常,服务器会在用户登录后创建一个唯一的会话标识符(Session ID),并将这个ID与用户的相关信息存储在服务器上的内存或持久化存储中。
状态管理:Session用于跟踪用户的会话状态,可以存储用户的登录状态、权限信息、购物车内容等。服务器可以根据Session ID来识别特定用户的会话,并提供相应的个性化服务。
Cookies支持:通常,服务器会将Session ID 存储在客户端的Cookie中,以便在后续的HTTP请求中发送回服务器。这样,服务器就可以根据Session ID 来识别用户的会话。但是,Session ID 也可以通过其他方式在客户端进行传输,比如通过URL参数或者隐藏域。
安全性:由于Session数据存储在服务器端,因此相对来说更加安全。但是,仍然需要注意Session劫持(Session Hijacking)和会话固定(Session Fixation)等攻击。
区别
- Session主要依赖于服务器端存储,用于跟踪用户的会话状态。
- Token主要依赖于客户端存储,包含了用户的身份验证和状态信息,并且被设计为无状态的、可传递的机制。