400网站建设推广,江苏网站制作企业,转团关系必须用电脑吗,网页版游戏排行榜枪文章前言
企业内部产品应用使用JWT作为用户的身份认证方式#xff0c;在对应用评估时发现了新的关于JWT的会话安全带来的安全问题#xff0c;后期再整理时又加入了之前遗留的部分JWT安全问题#xff0c;至此汇总成一篇完整的JWT文章
简单介绍
JWT(JSON Web Token)是一种用…文章前言
企业内部产品应用使用JWT作为用户的身份认证方式在对应用评估时发现了新的关于JWT的会话安全带来的安全问题后期再整理时又加入了之前遗留的部分JWT安全问题至此汇总成一篇完整的JWT文章
简单介绍
JWT(JSON Web Token)是一种用于身份认证和授权的开放标准它通过在网络应用间传递被加密的JSON数据来安全地传输信息使得身份验证和授权变得更加简单和安全JWT对于渗透测试人员而言可能是一种非常吸引人的攻击途径因为它们不仅是让你获得无限访问权限的关键而且还被视为隐藏了通往以下特权的途径例如:特权升级、信息泄露、SQLi、XSS、SSRF、RCE、LFI等
基础概念 JWSSigned JWT签名过的JWT JWKJWT的密钥也就是我们常说的SECRET JWEEncrypted JWT部分payload经过加密的JWT JKUJKU(JSON Web Key Set URL)是JWT Header中的一个字段字段内容是一个URI该URI用于指定用于验证令牌秘钥的服务器该服务器用于回复JWK X5UX5U是JWT Header中的一个字段指向一组X509公共证书的URL与JKU功能类似 X.509标准X.509标准是密码学里公钥证书的格式标准包括TLS/SSL(WWW万维网安全浏览的基石)在内的众多Internet协议都应用了X.509证书
基本结构
JWT(JSON Web Token)的结构由三部分组成分别是Header、Payload和Signature下面是每一部分的详细介绍和示例
Header
Header包含了JWT使用的算法和类型等元数据信息通常使用JSON对象表示并使用Base64编码Header中包含两个字段alg和typ alg(algorithm)指定了使用的加密算法常见的有HMAC、RSA和ECDSA等算法 typ(type)指定了JWT的类型通常为JWT
下面是一个示例Header
{alg: HS256,typ: JWT
}其中alg指定了使用HMAC-SHA256算法进行签名typ指定了JWT的类型为JWT
Payload
Payload包含了JWT的主要信息通常使用JSON对象表示并使用Base64编码Payload中包含三个类型的字段注册声明、公共声明和私有声明 公共声明(Public Claims)是自定义的字段用于传递非敏感信息例如:用户ID、角色等 私有声明(Private Claims)是自定义的字段用于传递敏感信息例如密码、信用卡号等 注册声明(Registered Claims)预定义的标准字段包含了一些JWT的元数据信息例如:发行者、过期时间等
下面是一个示例Payload
{sub: 1234567890,name: John Doe,iat: 1516239022
}其中sub表示主题name表示名称iat表示JWT的签发时间
Signature
Signature是使用指定算法对Header和Payload进行签名生成的用于验证JWT的完整性和真实性Signature的生成方式通常是将Header和Payload连接起来然后使用指定算法对其进行签名最终将签名结果与Header和Payload一起组成JWTSignature的生成和验证需要使用相同的密钥下面是一个示例Signature
HMACSHA256(base64UrlEncode(header) . base64UrlEncode(payload),secret)其中HMACSHA256是使用HMAC SHA256算法进行签名header和payload是经过Base64编码的Header和Payloadsecret是用于签名和验证的密钥最终将Header、Payload和Signature连接起来用句点(.)分隔就形成了一个完整的JWT下面是一个示例JWT其中第一部分是Header第二部分是Payload第三部分是Signature注意JWT 中的每一部分都是经过Base64编码的但并不是加密的因此JWT中的信息是可以被解密的
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c在线平台
下面是一个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) {// 构建 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库会通常提供一种验证令牌的方法和一种解码令牌的方法比如: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?usernamecarlos HTTP/1.1完成靶场的解答 签名用None
场景介绍
在JWT的Header中alg的值用于告诉服务器使用哪种算法对令牌进行签名从而告诉服务器在验证签名时需要使用哪种算法目前可以选择HS256即HMAC和SHA256JWT同时也支持将算法设定为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:peterStep 3登录之后可以看到如下界面 Step 4捕获到的数据报信息如下所示 截取JWT的第二部分对其进行base64解码:
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5MzQ5M30将上述的sub字段值更改为administrator
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTM0OTN9Step 4在使用wiener用户的凭据访问/admin是会提示401 Unauthorized Step 5将第一步分的alg参数改为none
eyJraWQiOiIyNmNlNGNmMi0wYjFhLTQzZTUtOWYzNy1kOTA2ZjkxZmY2MzkiLCJhbGciOiJSUzI1NiJ9更改之后的header部分
eyJraWQiOiIyNmNlNGNmMi0wYjFhLTQzZTUtOWYzNy1kOTA2ZjkxZmY2MzkiLCJhbGciOiJub25lIn0替换JWT Token中的第二部分为之前我们构造的信息同时移除签名部分再次请求数据获取到敏感数据链接 调用敏感链接移除用户信息完成解题操作 密钥暴力猜解
密钥介绍
在JT中密钥用于生成和验证签名因此密钥的安全性对JWT的安全性至关重要一般来说JWT有以下两种类型的密钥 对称密钥对称密钥是一种使用相同的密钥进行加密和解密的加密算法在JWT中使用对称密钥来生成和验证签名因此密钥必须保密只有发送方和接收方知道由于对称密钥的安全性取决于密钥的保密性因此需要采取一些措施来保护它 非对称密钥非对称密钥使用公钥和私钥来加密和解密数据在JWT中使用私钥生成签名而使用公钥验证签名由于公钥可以公开因此非对称密钥通常用于验证方的身份
下面是一个使用JWT和对称密钥的JAVA示例代码
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;public class JWTExample {private static final String SECRET_KEY mysecretkey; // 设置密钥public static void main(String[] args) {String token createJWT(123456); // 生成JWTSystem.out.println(token);String result parseJWT(token); // 验证JWTSystem.out.println(result);}public static String createJWT(String id) {// 设置JWT过期时间为1小时long nowMillis System.currentTimeMillis();Date now new Date(nowMillis);long expMillis nowMillis 3600000; // 1小时Date exp new Date(expMillis);// 生成JWTString token Jwts.builder().setId(id).setIssuer(issuer).setSubject(subject).setIssuedAt(now).setExpiration(exp).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();return token;}public static String parseJWT(String token) {// 验证JWT是否合法String result ;try {result Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getId();} catch (Exception e) {e.printStackTrace();}return result;}
}下面是一个使用JWT和非对称密钥的Java示例代码代码中使用了RSA算法生成非对称密钥对
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;public class JWTExample {private static final String ISSUER example.com;private static final String SUBJECT userexample.com;public static void main(String[] args) throws Exception {KeyPair keyPair generateKeyPair();String token createJWT(ISSUER, SUBJECT, keyPair.getPrivate());System.out.println(token);Claims claims parseJWT(token, keyPair.getPublic());System.out.println(claims.getIssuer());System.out.println(claims.getSubject());}public static String createJWT(String issuer, String subject, PrivateKey privateKey) {Date now new Date();Date expiration new Date(now.getTime() 3600 * 1000); // 1 hourreturn Jwts.builder().setIssuer(issuer).setSubject(subject).setIssuedAt(now).setExpiration(expiration).signWith(privateKey, SignatureAlgorithm.RS256).compact();}public static Claims parseJWT(String token, PublicKey publicKey) {return Jwts.parserBuilder().setSigningKey(publicKey).build().parseClaimsJws(token).getBody();}public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {KeyPairGenerator generator KeyPairGenerator.getInstance(RSA);generator.initialize(2048);return generator.generateKeyPair();}
}在这个示例中我们使用了Java中的KeyPairGenerator类来生成一个2048位的RSA密钥对然后使用私钥生成JWT使用公钥验证JWT在创建JWT时我们设置了JWT的颁发者、主题、签发时间和过期时间并使用signWith()方法和SignatureAlgorithm.RS256算法使用私钥进行签名在验证JWT时我们使用公钥来解析JWT并获取声明的内容在实际的研发编码中我们一方面要妥善保管密钥另一方面需要使用较为复杂难以被猜解的密钥作为密钥首选例如随机字母数字的32位长度组合
漏洞案例
在实现JWT应用程序时开发人员有时会犯一些错误比如忘记更改默认密码或占位符密码他们甚至可能复制并粘贴他们在网上找到的代码片段然后忘记更改作为示例提供的硬编码秘密在这种情况下攻击者使用众所周知的秘密的单词列表来暴力破解服务器的秘密是很容易的下面是一个公开已知密钥列表 https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list 在这里我们也建议使用hashcat来强力破解密钥您可以手动安装hashcat也可以在Kali Linux上使用预先安装好的hashcat您只需要一个来自目标服务器的有效的、签名的JWT和一个众所周知的秘密的单词表然后就可以运行以下命令将JWT和单词列表作为参数传入:
hashcat -a 0 -m 16500 jwt wordlistHashcat会使用单词列表中的每个密钥对来自JWT的报头和有效载荷进行签名然后将结果签名与来自服务器的原始签名进行比较如果有任何签名匹配hashcat将按照以下格式输出识别出的秘密以及其他各种详细信息由于hashcat在本地机器上运行不依赖于向服务器发送请求所以这个过程非常快即使使用一个巨大的单词表一旦您确定了密钥您就可以使用它为任何JWT报头和有效载荷生成有效的签名
jwt:identified-secret靶场地址https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key 实验步骤 Step 1点击上述Access the lab进入到靶场环境 Step 2使用以下账户进行登录操作
wiener:peterStep 3捕获到如下有效的JWT凭据信息
eyJraWQiOiI4M2RhOGNjMi1hZmZiLTRmZGMtYWMwYS1iMWNmMTBkNjkyZGYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5NjQwMn0.IhZV-7RHTpEcQvkcZOA3knCYmQD0YUg-NFMj9fWSFjwStep 5使用字典进行暴力猜解操作
方式一HashCat 项目地址https://github.com/hashcat/hashcat 项目使用
#命令格式
hashcat -a 0 -m 16500 jwt wordlist#执行示例
hashcat -m 16500 jwt.txt -a 0 secrets.txt --force方式二jwt_tool 项目地址https://github.com/ticarpi/jwt_tool 项目介绍此项目主要用于JWT安全脆弱性评估目前支持如下几种安全评估测试 (CVE-2015-2951) The algnone signature-bypass vulnerability (CVE-2016-10555) The RS/HS256 public key mismatch vulnerability (CVE-2018-0114) Key injection vulnerability (CVE-2019-20933/CVE-2020-28637) Blank password vulnerability (CVE-2020-28042) Null signature vulnerability Step 1克隆项目到本地
https://github.com/ticarpi/jwt_toolStep 2安装依赖库
pip3 install pycryptodomexStep 3运行jwt_tool并查看用法信息
python3 jwt_tool.py -h