• 渗透测试--JWT攻防(一)


    JWT简介

    JWT代表JSON Web Token,它是一种用于安全地在不同实体之间传递信息的开放标准(RFC 7519)。JWT通常用于身份验证和授权领域,以及在网络应用程序和服务之间传递声明(claims)信息。
    JWT的常见用途包括在身份验证流程中生成令牌,将用户信息传递给Web应用程序,以及在不同的服务之间进行身份验证和授权。由于JWT是自包含的,不需要在服务器端存储会话信息,因此它们适用于分布式系统和微服务架构。

    JWT结构

    JWT的结构是一个紧凑的、自包含的文本字符串,它由三个部分组成,这些部分使用点号(.)分隔开来,分别是HeaderPayloadSignature

    1.Header

    头部通常包含了关于令牌的元信息,例如使用的加密算法。这部分使用Base64编码,但未加密。header中有两个指定的字段:algtyp

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    
    • 1
    • 2
    • 3
    • 4

    alg(Algorithm):指定用于对JWT进行签名的加密算法。在这里,使用了HS256,它代表HMAC SHA-256算法,一种常见的对称加密算法。
    typ(Type):指定令牌的类型,通常设置为"JWT"表示这是一个JSON Web Token。

    2.Payload

    载荷(Payload):载荷包含了一些声明(claims),这些声明描述了实体(通常是用户)和其他数据。有三种类型的声明:

    • 注册声明(Registered claims):这些是预定义的标准声明,例如iss(发行者)、sub(主题)、exp(过期时间)等。
    • 私有声明(Private claims):这些声明是由用户定义的,用于在双方之间共享信息。
    • 公共声明(Public claims):这些声明用于共享信息,但它们应该是在被定义时以公开方式可用的,以避免冲突。
    {
      "sub": "1234567890",
      "name": "tuboshusec",
      "iat": 1697790142
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这个示例中,JWT的载荷部分是一个JSON对象,包含了一些声明(claims):

    • sub(Subject):表示令牌的主题,通常是用户的唯一标识符,如用户ID。
    • name:包含有关用户的姓名信息。
    • iat(Issued At):指定令牌的签发时间,以 UNIX 时间戳表示。

    3.Signature

    签名(Signature):签名部分用于验证令牌的完整性和真实性。它使用头部中指定的加密算法(如HMAC SHA256或RSA)对头部和载荷部分进行签名,以确保它们在传输过程中未被篡改。

    HMACSHA256(
      base64UrlEncode(header) + "." + base64UrlEncode(payload),
      secret
    )
    
    • 1
    • 2
    • 3
    • 4

    这个示例使用HMAC SHA-256算法生成JWT的签名,其中:

    • base64UrlEncode(header) 表示对JWT头部的Base64 URL编码。
    • base64UrlEncode(payload) 表示对JWT载荷的Base64 URL编码。
    • secret 是用于生成签名的密钥。
      生成的签名(Signature)示例(使用假设的密钥):
      3DyCO9ZQpXGbp7ZhSJxKQAWKz-dDWy2oGmjNRhzd_6I
      现在,将这些部分组合在一起形成一个完整的JWT,使用点号分隔各个部分:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6InR1Ym9zaHVzZWMiLCJpYXQiOjE2OTc3OTAxNDJ9.f9B4-JRPyMGMiL2syuhuB9k0mZV4mhTN72MZesAn-tQ
    
    • 1

    这是一个完整的JWT,包括头部、载荷和签名部分。JWT的实际用途是在身份验证和授权流程中传递信息,并确保令牌的完整性和真实性。在实际应用中,密钥将用于生成和验证签名部分。可以看到JWT是base64编码的,并不是加密的,所以JWT可以被解密。

    JWT解密

    该网站可进行JWT在线加解密
    https://jwt.io/
    image.png

    JWT工作原理

    生成令牌:

    • 创建头部(Header):首先,选择一个加密算法(例如HS256或RS256),并将其与令牌类型(通常是"JWT")组成头部。头部通常以JSON格式表示,并使用Base64 URL编码。
    • 创建载荷(Payload):在载荷中,包含声明(claims)信息,这些声明可以是注册声明、私有声明或公共声明。注册声明包括标准字段如iss(发行者)、sub(主题)、exp(过期时间)等,而私有声明用于应用程序特定信息。载荷也以JSON格式表示,并使用Base64 URL编码。
    • 生成签名(Signature):签名是用于验证令牌的完整性和真实性的关键部分。签名的生成依赖于选择的加密算法和一个密钥。通常,签名由将头部和载荷组合在一起,并使用密钥进行签名的过程生成。
    • 组合三部分:将头部、载荷和签名用点号分隔并组合成一个JWT字符串。

    传输和验证令牌:

    • 传输令牌:JWT可以在网络请求的标头、URL参数或请求体中传输。它通常被发送给服务或资源,以证明用户的身份或授权访问。
    • 接收令牌:接收方接收到JWT后,将其分成头部、载荷和签名三个部分。
    • 验证签名:接收方使用与生成令牌时相同的加密算法和密钥,将头部和载荷部分重新组合,然后生成一个新的签名。接着,将新生成的签名与原始JWT中的签名进行比较。如果签名匹配,那么JWT是有效的。如果签名不匹配,JWT被认为是无效的,或者可能已被篡改。
    • 验证声明:接收方还可以验证载荷中的声明,例如检查令牌是否过期(通过比较"exp"声明与当前时间)以及其他声明来确保授权和身份验证操作的有效性。

    JWT的工作原理允许在不同服务之间传递身份验证和授权信息,而无需在服务器端存储会话状态。这使得JWT在分布式系统和微服务架构中非常有用。同时,使用正确的安全实践和保护密钥是确保JWT安全性的关键部分。

    JAVA

    在java中JWT库可以很容易实现JWT签名和验证
    使用JWT库要在Maven或Gradle中添加依赖,在Maven中,可以将以下依赖添加到pom.xml文件中:

    
        io.jsonwebtoken
        jjwt
        0.9.1 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这是一个简单的JWT签名和验证的示例代码:

    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import io.jsonwebtoken.Claims;
    
    public class JwtExample {
    
        // 密钥,实际应用中应该保护好密钥,不要硬编码在代码中
        private static final String SECRET_KEY = "mySecretKey";
    
        public static void main(String[] args) {
            // 创建JWT
            String jwt = createJWT("1234567890", "John Doe");
    
            System.out.println("Generated JWT: " + jwt);
    
            // 验证JWT
            Claims claims = parseJWT(jwt);
            if (claims != null) {
                System.out.println("Subject: " + claims.getSubject());
                System.out.println("Name: " + claims.get("name"));
            } else {
                System.out.println("JWT verification failed.");
            }
        }
    
        // 创建JWT
        private static String createJWT(String subject, String name) {
            return Jwts.builder()
                    .setSubject(subject)
                    .claim("name", name)
                    .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                    .compact();
        }
    
        // 验证JWT
        private static Claims parseJWT(String jwt) {
            try {
                return Jwts.parser()
                        .setSigningKey(SECRET_KEY)
                        .parseClaimsJws(jwt)
                        .getBody();
            } catch (Exception e) {
                // 验证失败
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在上面的示例代码中使用jwt库进行JWT的签名和验证,首先构建了一个JWT,然后将其分离为Header、Payload和Signature三部分,使用parseClaimsJws函数对JWT进行解析和验证,从而获取其中的Payload中的信息并进行验证.

    JWT攻击思路

    一:伪造令牌

    我们这里用burpsuite的靶场进行学习:
    靶场地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature
    image.png
    这是提示,告诉我们要做什么
    image.png
    image.png
    image.png
    账号密码为wiener/peter
    image.png
    登录
    image.png
    bp里有这样的数据包
    image.png
    可以看到session是一个标准的JWT形式,直接解密看看
    image.png
    我们把中间紫色部分即payload部分sub用户改成administrator,然后编码一下替换原版的payload部分,注意编码的时候别有空格哦
    image.png
    image.png
    题目要求访问/admin路径
    image.png
    有两个删除用户的接口,进行删除操作
    image.png
    image.png
    删除成功,这道题就是我们利用JWT可以被解密的特性,伪造了administrator用户的JWT,实现从普通用户到administrator权限的一个越权操作。

    二:签名用None

    靶场地址:
    https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-flawed-signature-verification
    image.png
    还是一样的要求,要删除carlos用户,和上一题一样的操作,登录进来先
    image.png
    image.png
    看似和上题一样的数据包,尝试替换administrator的payload,伪造JWT去请求/admin
    image.png
    image.png
    这里返回了401,这里就是和上一题不一样的地方,解决方法也很简单。去解密一下JWT的第一部分即header部分
    image.png
    将alg至为none再进行编码替换
    image.png
    再进行一步URL编码
    image.png
    此时再替换header,并将SIGNATURE签名去掉,只留下header和payload部分
    image.png
    这时返回200,也返回了删除用户的接口,拿着伪造后的JWT访问接口
    image.png
    image.png
    成功通关,这一关是利用了如果"alg"字段设为"None",则标识不签名,这样一来任何token都是有效的,设定该功能的最初目的是为了方便调试,但是若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为"None"来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站

    ***************未完待续

  • 相关阅读:
    【备忘录】修复docker环境下的nacos安全漏洞:身份认证绕过漏洞
    【运维自动化-配置平台】如何通过模板创建集群和模块
    外包干了3个月,技术退步明显。。。。。
    创宇区块链 | 6 月安全月报
    阿里 P8 级别面试官分享出源码阅读技巧附 Java 源码和大厂真题
    加拿大海运专线怎么选?加拿大海运专线有哪些费用
    60V降压恒流芯片 高调光比LED驱动器 SL6015B替代PT4115 电路简单
    如何申请成为抖音本地生活服务商?一文详细步骤手把手教你入驻!
    软件工程概述--敏捷宣言
    Redis-key的基本命令和Redis最常用的数据类型:String
  • 原文地址:https://blog.csdn.net/qq_53003652/article/details/133966069