关于JWT的攻击利用
关于JWT的攻击利用
- 一、JWT简介
- 二、基本结构
- 1、Header(头部)
- 2、Payload(载荷)
- 3、Signature(签名)
- 三、JWT工作原理
- 1、生成令牌
- 2、传输令牌
- 3、验证令牌
- 四、java实现JWT签名验证
- 五、JWT攻击利用
- 1、伪造令牌
- 2、签名用none
- 3、弱密钥爆破
- 五、漏洞利用工具
一、JWT简介
JWT(JSON Web Token)是一种用于身份认证和授权的开放标准,它通过在网络应用间传递被加密的JSON数据来安全地传输信息使得身份验证和授权变得更加简单和安全。
JWT的常见用途包括在身份验证流程中生成令牌,将用户信息传递给Web应用程序,以及在不同的服务之间进行身份验证和授权。由于JWT是自包含的,不需要在服务器端存储会话信息,因此它们适用于分布式系统和微服务架构。
二、基本结构
JWT(JSON Web Token)的结构由三部分组成,分别是Header、Payload和Signature。如下图所示:
1、Header(头部)
Header包含了JWT使用的算法和类型等元数据信息,通常使用JSON对象表示并使用Base64编码,Header中包含两个字段:alg和typ
Header示例:
{"alg": "HS256","typ": "JWT"
}
alg(Algorithm):指定用于对JWT进行签名的加密算法,常见的有HMAC、RSA和ECDSA等算法。在这里,使用了HS256,它代表HMAC SHA-256算法,一种常见的对称加密算法。
typ(Type):指定令牌的类型,通常设置为"JWT"表示这是一个JSON Web Token。
2、Payload(载荷)
Payload包含了JWT的主要信息,通常使用JSON对象表示并使用Base64编码,Payload中包含三个类型的字段:注册声明、公共声明和私有声明
- 公共声明(Public Claims):这些声明用于共享信息,但它们应该是在被定义时以公开方式可用的,以避免冲突。例如:用户ID、角色等
- 私有声明(Private Claims):这些声明是由用户定义的,用于在双方之间共享信息。例如密码、信用卡号等
- 注册声明(Registered Claims):预定义的标准字段,包含了一些JWT的元数据信息,例如iss(发行者)、sub(主题)、exp(过期时间)等。
Payload示例:
{"sub": "1234567890","name": "MP-Fly","iat": 1516239022
}
在这个示例中,JWT的载荷部分是一个JSON对象,包含了一些声明(claims):
sub(Subject):表示令牌的主题,通常是用户的唯一标识符,如用户ID。
name:包含有关用户的姓名信息。
iat(Issued At):指定令牌的签发时间,以 UNIX 时间戳表示。
3、Signature(签名)
Signature是用于验证令牌的完整性和真实性。它使用头部中指定的加密算法(如HMAC SHA256或RSA)对头部和载荷部分进行签名,以确保它们在传输过程中未被篡改。Signature的生成方式通常是将Header和Payload连接起来然后使用指定算法对其进行签名,最终将签名结果与Header和Payload一起组成JWT,Signature的生成和验证需要使用相同的密钥。
Signature示例:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret
)
这个示例使用HMAC SHA-256算法生成JWT的签名,其中:
base64UrlEncode(header) 表示对JWT头部的Base64 URL编码。
base64UrlEncode(payload) 表示对JWT载荷的Base64 URL编码。
secret 是用于生成签名的密钥。
生成的签名(Signature)示例(使用假设的密钥):
!23QweAsd
最终将Header、Payload和Signature连接起来用句点(.)分隔就形成了一个完整的JWT,下面是一个示例JWT,其中第一部分是Header,第二部分是Payload,第三部分是Signature。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1QLUZseSIsImlhdCI6MTUxNjIzOTAyMn0.cQ37gZ5oVgE6SGb6L9roV_QSqyKSGcW1Q_3Xr4-73Ec
注意JWT 中的每一部分都是经过Base64编码的,但并不是加密的,因此JWT中的信息是可以被解密的。
三、JWT工作原理
1、生成令牌
-
(1)创建头部(Header):首先,选择一个加密算法(例如HS256或RS256),并将其与令牌类型(通常是"JWT")组成头部。头部通常以JSON格式表示,并使用Base64 URL编码。
-
(2)创建载荷(Payload):在载荷中,包含声明(claims)信息,这些声明可以是注册声明、私有声明或公共声明。注册声明包括标准字段如iss(发行者)、sub(主题)、exp(过期时间)等,而私有声明用于应用程序特定信息。载荷也以JSON格式表示,并使用Base64 URL编码。
-
(3)生成签名(Signature):签名由将头部和载荷组合在一起,并使用密钥进行签名的过程生成。
-
(4)合并三部分:将头部、载荷和签名用点号分隔并组合成一个JWT字符串。
2、传输令牌
- (1)发送令牌:将生成的JWT发送给需要访问信息的一方,通常通过HTTP标头进行传输。令牌可以放在请求的授权标头中(Bearer Token)
- (2)接收令牌:接收方接收到JWT后,将其分成头部、载荷和签名三个部分。
3、验证令牌
-
(1)验证签名: 接收方使用相同的密钥和算法对头部和负载进行签名,并比较生成的签名与JWT中的签名是否匹配。如果匹配,令牌有效。
-
(2)验证声明:接收方还需验证载荷中的声明,例如检查令牌是否过期(通过比较"exp"声明与当前时间)以及其他声明来确保授权和身份验证操作的有效性。
-
(3)令牌有效性: 如果签名验证通过且声明有效,则令牌有效;否则,拒绝令牌并执行相应的安全措施。
四、java实现JWT签名验证
使用JWT库要在Maven或Gradle中添加依赖,在Maven中,可以将以下依赖添加到pom.xml文件中:
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.5.0</version></dependency>
JWT签名和验证的示例代码:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import java.util.Date;public class JWTExample {private static final String SECRET_KEY = "my_secret_key";public static void main(String[] args) {// 构建 JWTString jwtToken = Jwts.builder().setSubject("1234567890").claim("name", "John Doe").setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();// 验证 JWTtry {// 分离 Header, Payload 和 SignatureString[] jwtParts = jwtToken.split("\\.");String header = jwtParts[0];String payload = jwtParts[1];String signature = jwtParts[2];// 验证签名String expectedSignature = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(jwtToken).getSignature();if (!signature.equals(expectedSignature)) {throw new RuntimeException("Invalid JWT signature");}// 验证 Payload 中的信息Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(jwtToken).getBody();System.out.println("Valid JWT");} catch (Exception e) {System.out.println("Invalid JWT: " + e.getMessage());}}
}
在上面的示例代码中使用jwt库进行JWT的签名和验证,首先构建了一个JWT,然后将其分离为Header、Payload和Signature三部分,使用parseClaimsJws函数对JWT进行解析和验证,从而获取其中的Payload中的信息并进行验证,最后如果解析和验证成功,则说明JWT是有效的,否则说明JWT是无效的,在实际应用中应该将SECRET_KEY替换为应用程序的密钥。
五、JWT攻击利用
1、伪造令牌
靶场地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature
账号密码为wiener/peter,点击登录进行抓包
可以看到session是JWT形式,直接解密.
将payload部分sub用户改成administrator,然后编码一下替换原版的payload部分,
替换掉之前的payload
要完成题目的要求访问/admin路径
有两个删除用户的接口,进行删除操作
删除成功,这道题就是我们利用JWT可以被解密的特性,伪造了administrator用户的JWT,实现从普通用户到administrator权限的一个越权操作。
2、签名用none
靶场地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-flawed-signature-verification
还是一样的要求,要删除carlos用户,和上一题一样的操作,先登录
登录之后,替换payload发现返回401报错
这一题首先去解密一下JWT的第一部分即header部分
将alg至为none再进行编码替换
此时再替换header,并将SIGNATURE签名去掉,只留下header和payload部分(header中替换alg为none,payload中替换winer为administraor),保留结尾的点.
这时可以访问删除用户的接口
成功通关,这一关是利用了如果"alg"字段设为"none",则标识不签名,这样一来任何token都是有效的,攻击者可以通过将alg字段设置为"none"来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站
3、弱密钥爆破
还是一样登录进去,获取jwt
这里使用了弱密钥,这时候就可以通过爆破弱密钥来进行攻击,获取弱密钥和加密算法后,就能进行伪造签名。
获取到密钥后,打开https://jwt.io/重新生成一个jwt
修改前
在左侧填入原始的jwt,随后在右侧,修改账号为administrator,在右侧下面填入秘钥,然后就会在左边生成新的对应的jwt。修改后,复制左侧的jwt然后进行替换到数据包。
完成!
五、漏洞利用工具
1、jwt.io
https://jwt.io/:一个在线工具,可以解析和调试 JWT,帮助开发者查看 JWT 的内容和签名算法,识别常见问题。
2、jwt_tool:
https://github.com/ticarpi/jwt_tool:用于分析、生成和攻击 JWT 的工具,支持算法混淆攻击等多种手段。
3、jwtcrack:
https://github.com/Sjord/jwtcrack:字典枚举破解 HS256, HS384 或 HS512 加密算法的JWT