网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
##### 在线平台
下面是一个JWT在线构造和解构的平台:
https://jwt.io/

#### 工作原理
##### JWT工作原理
JWT的工作流程如下:
* 用户在客户端登录并将登录信息发送给服务器
* 服务器使用私钥对用户信息进行加密生成JWT并将其发送给客户端
* 客户端将JWT存储在本地,每次向服务器发送请求时携带JWT进行认证
* 服务器使用公钥对JWT进行解密和验证,根据JWT中的信息进行身份验证和授权
* 服务器处理请求并返回响应,客户端根据响应进行相应的操作
##### JKU工作原理
Step 1:用户携带JWS(带有签名的JWT)访问应用

Step 2:应用程序解码JWS得到JKU字段

Step 3:应用根据JKU访问返回JWK的服务器

Step 4:应用程序得到JWK

Step 5:使用JWK验证用户JWS

Step 6:验证通过则正常响应

#### 漏洞攻防
##### 签名未校验
###### 验证过程
JWT(JSON Web Token)的签名验证过程主要包括以下几个步骤:
* 分离解构:JWT的Header和Payload是通过句点(.)分隔的,因此需要将JWT按照句点分隔符进行分离
* 验证签名:通过使用指定算法对Header和Payload进行签名生成签名结果,然后将签名结果与JWT中的签名部分进行比较,如果两者相同则说明JWT的签名是有效的,否则说明JWT的签名是无效的
* 验证信息:如果JWT的签名是有效的则需要对Payload中的信息进行验证,例如:可以验证JWT中的过期时间、发行者等信息是否正确,如果验证失败则说明JWT是无效的
下面是一个使用JAVA进行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) {
// 构建 JWT
String 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();
// 验证 JWT
try {
// 分离 Header, Payload 和 Signature
String\[\] 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库会通常提供一种验证令牌的方法和一种解码令牌的方法,比如:Node.js库jsonwebtoken有verify()和decode(),有时开发人员会混淆这两种方法,只将传入的令牌传递给decode()方法,这意味着应用程序根本不验证签名,而我们下面的使用则是一个基于JWT的机制来处理会话,由于实现缺陷服务器不会验证它收到的任何JWT的签名,如果要解答实验问题,您需要修改会话令牌以访问位于/admin的管理面板然后删除用户carlos,您可以使用以下凭据登录自己的帐户:wiener:peter
靶场地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature

演示步骤:
Step 1:点击上方的"Access the Lab"访问靶场并登录账户

Step 2:进入到Web界面并登录靶场账户
wiener:peter


登录之后会看到如下一个更新邮箱的界面

Step 3:此时在我们的burpsuite中我们可以看到如下的会话信息

此时查询当前用户可以看到会显示当前用户为wiener:

截取上面中间一部分base64编码的部分更改上面的sub为"administrator"
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5MDA4M30

构造一个sub为"administrator"的载荷并将其进行base64编码处理:
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTAwODN9

替换之后重新发送请求:

按照题目要求访问/admin路径,发现两个删除用户的调用接口:

请求敏感链接——删除用户carlos
GET /admin/delete?username=carlos HTTP/1.1

完成靶场的解答:

##### 签名用None
###### 场景介绍
在JWT的Header中alg的值用于告诉服务器使用哪种算法对令牌进行签名,从而告诉服务器在验证签名时需要使用哪种算法,目前可以选择HS256,即HMAC和SHA256,JWT同时也支持将算法设定为"None",如果"alg"字段设为"None",则标识不签名,这样一来任何token都是有效的,设定该功能的最初目的是为了方便调试,但是若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为"None"来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站
{
“alg”: “none”,
“typ”: “JWT”
}
###### 漏洞案例
实验靶场:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-flawed-signature-verification

实验流程:
Step 1:点击上方的"Access the lab"访问靶场环境
https://0a9c00a8030ba77784d7b92d00cc0086.web-security-academy.net/

Step 2:使用账户密码进行登录
wiener:peter

Step 3:登录之后可以看到如下界面
![](https://img-blog.csdnimg.cn/img_convert/17