揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证
揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证
介绍
JWT(JSON Web Tokens)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在网络上安全地传输信息。这种信息可以验证和信任,因为它是数字签名的。JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
「优点」:
「无状态」:服务器不需要保存会话信息,减轻了服务器负担。 「可扩展性」:易于在分布式系统中使用,支持跨域身份验证。 「安全性」:通过数字签名确保信息的完整性和来源可信。
「缺点」:
「令牌大小」:由于包含头部、负载和签名,JWT的大小可能相对较大。 「性能」:每次请求都需要验证JWT,可能会增加服务器的处理时间。 「敏感信息泄露」:虽然Payload部分可以加密,但如果不当处理,仍可能泄露敏感信息。
简单复现
CTFShow-web345

让我们查看网页源码,貌似是告诉我们有这个admin后台页面

抓包

修改请求头为admin/index.php

将第一段进行解码,发现是jwt的第一段编码配置,但是加密方式为None,这段token也没有第三段,只有两段,说明根本没有进行加密

第二段就是payload,元数据

尝试将用户user改为admin获取后台权限

302重定向了

尝试把jwt的配置信息给删掉

成功了

python详解jwt
JWT(JSON Web Tokens)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在网络上安全地传输信息。这种信息可以验证和信任,因为它是数字签名的。JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。
它的主要应用场景:
授权:这是JWT最常见的使用场景。一旦用户登录,每个后续请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录(SSO)是目前广泛使用JWT的一项特性,因为它的开销很小,并且可以轻松地跨域使用。 信息交换: JWT是在各方之间安全传输信息的好方式。因为JWT可以被签名,例如,使用公钥/私钥对,你可以确定发送方就是它们所说的那个人。此外,由于签名是使用标头和有效负载计算的,您还可以验证内容是否被篡改。
python代码示例
import jwt
import time
# 设置headers,即加密算法的配置
headers = {
"alg": 'HS256',
'typ': 'JWT'
}
key = 'abcdefghijklmnopqrstuvwxyz'
# 随机的salt密匙,只有token生成者(同时也是校验者)自己能有,用于校验生成的token是否合法
exp = int(time.time()+1) # 设置过期时间, time.time()返回当前时间的时间戳,*1000即可得到毫秒的时间戳
payload = {
'name': 'xiaoyu', # 这个名称可以进行加密
'exp': exp
}
# 配置主题信息,一般是登录成功的用户之类的,因为jwt的主题信息很容易被解码,所以不要放敏感信息
# 当然也可以将敏感信息加密后再放进payload
# 生成token
token = jwt.encode(payload=payload, key=key, headers=headers)
print(token)
# 将token进行解码,第二个参数key用于校验
info = jwt.decode(token, key,algorithms=['HS256'])
print(info)
#等待两秒后再次验证token,因超时将导致验证失败
time.sleep(2)
try:
info=jwt.decode(token, key, algorithms=['HS256'])
print(info)
except Exception as e:
print(e)
info = jwt.decode(token, key, algorithms=['HS256'],options={'verify_signature': False})
print(info)
# 结果
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb3l1IiwiZXhwIjoxNzE5OTE3NzgwfQ.TGzJclF64WW_Tpxm_p84IYhegL2Pjun5CXbMuizLnzA
# {'name': 'xiaoyu', 'exp': 1719917780}
# Signature has expired
# {'name': 'xiaoyu', 'exp': 1719917780}
jwt生成token所需要的字段
JWT由三部分组成:Header(头部)、Payload(负载)和Signature(签名)。
headers:头部通常包含两部分:令牌的类型(即JWT)和所使用的哈希算法(如HMAC SHA256或RSA)。 payload:负载包含了Claim,Claim是一些实体(通常指用户)的状态和额外的元数据,有三种类型的Claim:注册Claim、公共Claim和私有Claim。

❝
iss:jwt签发者
sub:jwt所面向的用户
aud:接收jwt的一方
exp:jwt的过期时间,这个过期时间必须大于签发时间
nbf:定义在什么时间之前,该jwt都是不可用的
iat:jwt的签发时间
jti:jwt的唯一标识身份,主要用来作为一次性token,从而回避重放攻击。
❞
secret_key: 签名是对Header和Payload的签名,防止数据篡改。首先,需要将Header和Payload使用Base64编码,然后用 .连接,之后使用Header中指定的签名算法(HS256)进行签名。
jwt生成token的过程
由上面的简单示例可以看出,jwt生成token主要由三部分,用.号隔开,分别代表:编码后的headers、payload,以及校验字段
通过对
headers的json数据进行base64url编码生成第一部分通过对
payload的json数据进行base64url编码生成第二部分将第一部分和第二部分通过
.拼接起来,然后对拼接后的内容结合签名密钥进行HS256加密生成密文(加密算法可以自己选,默认HS256),然后再进行base64url编码,从而生成第三部分三个部分通过
.拼接起来,作为token
❝
base64url编码:先进行base64编码,然后将其中的
+替换成-、/替换成_,并且最后一般会将=都去掉❞
例如,下面是一个加密后的payload
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb3l1IiwiZXhwIjoxNzE5OTE3NzgwfQ.TGzJclF64WW_Tpxm_p84IYhegL2Pjun5CXbMuizLnzA
------------------------------------ ---------------------------------------------- -------------------------------------------
-------------- headers ------------- -------- payload --------------------------- --------------- 校验字段--------------------
解密后

将各段尝试使用base64进行解密,第一段headers
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
base64解密后:{"alg":"HS256","typ":"JWT"}
第二段payload
eyJuYW1lIjoieGlhb3l1IiwiZXhwIjoxNzE5OTE3NzgwfQ
base64解密后:{"name":"xiaoyu","exp":1719917780}
第三段校验字段
TGzJclF64WW_Tpxm_p84IYhegL2Pjun5CXbMuizLnzA
base64解密后:Ll�rQz�e������ ���lˢ̹�
jwt异常处理
jwt校验抛出的异常类基本都在jwt和jwt.exceptions下,举例:
import jwt
import time
from jwt import exceptions
# 配置第一段
headers = {
'tyb': 'JWT',
'alg': 'HS256'
}
salt = 'demmskfhkjagh' # 随便输入即可
exp = int(time.time() + 1) # jwt过期时间-1为立即失效
payload = { # 你需要加密的数据
'name': 'xiaoyu',
'password': '123456',
'age': '21',
'gender': 'man',
'exp': exp
}
token=jwt.encode(payload, salt, headers=headers)
time.sleep(2)# 延迟2s使token过期
try:
info=jwt.decode(token,salt,algorithms=['HS256'])
print(info)
except exceptions.ExpiredSignatureError:
print('token已过期')
except jwt.DecodeError:
print('token认证失败')
except jwt.InvalidTokenError:
print('非法的token')
Web开发中简单示例
传统token
from flask import Flask, request
import json
import uuid
app = Flask(__name__)
db_source = {
'user_table': {
'xiaoyu': {
'pwd': '123456'
}
},
'user_token': {},
'user_info_table': {
'xiaoyu': {
'age': 21
}
}
}
@app.route("/login/<username>/<password>/")
def login(username, password):
if (not username) or (not password):
return {'status': 1, 'code': '400', 'msg': '用户或密码不允许为空!'}
if not db_source['user_table'].get(username, None):
return {'status': 1, 'code': '401', 'msg': '用户不存在!'}
if db_source['user_table'][username]['pwd'] != password:
return {'status': 1, 'code': '402', 'msg': '用户名或密码错误!'}
token = str(uuid.uuid4())
db_source['user_token'][token] = username
return {'status': 1, 'code': '200', 'data': {'token': token}}
@app.route('/user_info', methods=['GET'])
def user_info():
'''查看用户信息,需要和本地保存的token进行校验'''
token = request.args.get('token', None)
if not token:
return json.dumps({'status': 1, 'code': '500', 'msg': 'token不允许为空!'}, ensure_ascii=False)
if not db_source['user_token'].get(token, None):
return json.dumps({'status': 1, 'code': '501', 'msg': 'token校验失败!'}, ensure_ascii=False)
# 当token校验成功则返回用户信息
username = db_source['user_token'][token]
info = db_source['user_info_table'][username]
return json.dumps({'status': 1, 'code': '200', 'data': {username: info}})
if __name__ == '__main__':
app.run(debug=True)
测试效果我们的用户名为xiaoyu,密码为123456





可以发现这个基于uuid的token使用过之后没有过期时间,永久的存储非常的不方便,数据库也要新加一个字段token有效期,而jwt可以很好的解决这个问题
基于jwt
import jwt
from flask import Flask, request
import json
import uuid
from jwt import exceptions
app = Flask(__name__)
db_source = {
'user_table': {
'xiaoyu': {
'pwd': '123456'
}
},
'user_info_table': {
'xiaoyu': {
'age': 21
}
}
}
SECRET_KEY = "ljksljfasiieksf"
# @app.route('/create_token/<name>')
def create_token(name):
'''
基于jwt创建token函数
:param name:
:return:
'''
global SECRET_KEY
# 加密配置
headers = {
'alg': 'HS256',
'typ': 'JWT'
}
exp = int(time.time() + 20) # 配置token有效时间为20s
payload = {
'name': name, # 这里只配置用户名或者用户id,防止信息泄露
'exp': exp
}
token = jwt.encode(payload=payload, key=SECRET_KEY, headers=headers)
return token
def validate_token(token):
'''
校验token
:param token:
:return:
'''
payload=None
msg=None
try:
payload=jwt.decode(token,SECRET_KEY,algorithms=['HS256'])
except jwt.DecodeError as e:
msg='token verify failed!' # 错误
except jwt.ExpiredSignatureError:
msg='token lose efficacy!' # 失效
except jwt.InvalidTokenError:
msg='token unlawfulness!' # 非法
return (payload,msg)
@app.route("/login/<username>/<password>/")
def login(username, password):
if (not username) or (not password):
return {'status': 1, 'code': '400', 'msg': 'user or pass is None!'}
if not db_source['user_table'].get(username, None):
return {'status': 1, 'code': '401', 'msg': 'not user!'}
if db_source['user_table'][username]['pwd'] != password:
return {'status': 1, 'code': '402', 'msg': 'username or password error!'}
# token = str(uuid.uuid4())
# db_source['user_token'][token] = username
token=create_token(username)
return {'status': 1, 'code': '200', 'data': {'token': token}}
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb3l1IiwiZXhwIjoxNzE5OTk1MDM4fQ.siRTK3IKV4uhgFmEUMvfxBfmKqjg7G4B7eeXatP-Q-M
@app.route('/user_info', methods=['GET'])
def user_info():
'''查看用户信息,需要和本地保存的token进行校验'''
token = request.args.get('token', None)
if not token:
return {'status': 1, 'code': '500', 'msg': 'token is NULL!'}
# 校验token,并进行解码
payload,msg=validate_token(token)
if msg:
return {'status': 1, 'code': '501', 'msg': msg}
# 获取解码后的用户名
username = payload['name']
# 从数据源中读取解码后的用户信息
info = db_source['user_info_table'][username]
return {'status': 1, 'code': '200', 'data': {username: info}}
if __name__ == '__main__':
app.run(debug=True)
文章参考:https://www.jianshu.com/p/03ad32c1586c
原文链接:https://mp.weixin.qq.com/s/RT2SNHlrCcbA8IazJ6usqQ
揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证的更多相关文章
- 传智播客微金所项目实战移动web开发
1.源码笔记 我的源码+笔记(很重要):链接: http://pan.baidu.com/s/1kULKqcJ 感谢传智播客项目相关视频:1.6天 链接: https://pan.baidu.com/ ...
- JFinal极速开发实战-业务功能开发-通用表单验证器
提交表单数据时,需要经过前端的验证才能提交到后台,而后台的验证器再做一道数据的校验,成功之后才能进入action进行业务数据的处理. 在表单数据的验证中,数据类型的验证还是比较固定的.首先是对录入数据 ...
- JFinal Web开发学习(六)验证码验证和注册细节
效果: 实现了注册界面的验证码验证.确认密码.密码md5加盐加密.C3P0插件数据库操作.读取外部配置文件. 1.在注册页面添加了确认密码输入框,修改了字段名称 <!DOCTYPE html&g ...
- web开发常用的js验证,利用正则表达式验证邮箱、手机、身份证等输入
正则表达式验证 //邮箱 \-])+\.)+([a-zA-Z0-]{,})+$/; email = document.getElementById("email").value; ...
- JWT—JSON Web Token - 理解JWT网络间应用用户安全认证交互设计
原文地址:http://blog.leapoahead.com/2015/09/06/understanding-jwt/ 官网地址:https://jwt.io/ JSON Web Token(JW ...
- 《Java web 开发实战经典》读书笔记
去年年末,也就是大四上学期快要结束的时候,当时保研的事情确定了下来,终于有了一些空闲的时间可以学点实用的技术. 之前做数据库课程设计的时候,也接触过java web的知识,当时做了一个卖二手书籍的网站 ...
- 《Flask Web开发实战:入门、进阶与原理解析(李辉著 )》PDF+源代码
一句话评价: 这可能是市面上(包括国外出版的)你能找到最好的讲Flask的书了 下载:链接: https://pan.baidu.com/s/1ioEfLc7Hc15jFpC-DmEYBA 提取码: ...
- Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 )购买链接
Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 )购买链接: 推荐给你高品质的实战课程 https://laravel-china.org/courses?rf=158 ...
- HTML5移动Web开发实战 PDF扫描版
<HTML5移动Web开发实战>提供了应对这一挑战的解决方案.通过阅读本书,你将了解如何有效地利用最新的HTML5的那些针对移动网站的功能,横跨多个移动平台.全书共分10章,从移动Web. ...
- 关于测试驱动的开发模式以及实战部分,建议看《Python Web开发测试驱动方法》这本书
关于测试驱动的开发模式以及实战部分,建议看<Python Web开发测试驱动方法>这本书
随机推荐
- oeasy教您玩转vim - 65 - # 批处理操作
批处理操作 回忆上次 我们上次参数列表 arguments list 所谓参数列表指的是 vim 打开的 参数列表 参数会加载到内存中成为 buffer 参数的控制 :arga filename ...
- 深入探究 Golang 反射:功能与原理及应用
Hi 亲爱的朋友们,我是 k 哥.今天,咱们来一同探讨下 Golang 反射. Go 出于通用性的考量,提供了反射这一功能.借助反射功能,我们可以实现通用性更强的函数,传入任意的参数,在函数内通过反射 ...
- ComfyUI进阶:Comfyroll插件 (七)
前言: 学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业的图像生成与编辑工具.借助这些节点,用户可以在静态图 ...
- 如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志)
如何让SQL Server像MySQL一样拥有慢查询日志(Slow Query Log慢日志) SQL Server一直以来被人诟病的一个问题是缺少了像MySQL的慢日志功能,程序员和运维无法知道数据 ...
- Gradle配置文件解析和使用Meven本地仓库
Gradle配置文件 使用Gradle创建好项目之后,项目的根目录下会有一个build.gradle文件,该文件就是Gradle的核心配置文件 对应的信息: plugins { id 'java' } ...
- SQL提高查询性能的几种方式
创建索引,提高性能 索引可以极大地提高查询性能,其背后的原理: 索引是的数据库引擎能够快速的找到表中的数据,它们类似于书籍的目录,使得你不需要逐页查找所需要的信息 索引能够帮助数据库引擎直接定位到所需 ...
- Parallel and Sequential Data Structures and Algorithms
并串行 从零开始考前突击并串行数据结构与算法 强烈建议和原教材参照着看 Introduction 本书的要点 定义问题 不同的算法解决 设计抽象数据类型和相应的数据结构实现 分析比较算法和数据类型的代 ...
- NVIDIA一直宣传的DPU是个啥东西,啥用处? —— NVIDIA BlueField-3 DPU
地址: https://www.bilibili.com/video/BV1ys4y1z7nS/ 无意间看到了些比较靠谱的解释: (来自地址:https://www.bilibili.com/vide ...
- 【转载】 取消idea双击shift时出现的全局搜索 (使用pycharm IDE时出现的问题)
原文地址: https://blog.csdn.net/hxy199421/article/details/83030603 最近在使用pycharm的时候出现文章标题的问题,由于以前写的代码都是比较 ...
- 苹果系统Mac升级后之前的网络软件不可用——Mac系统维护——Mac系统升级后软件报错——mac系统升级后导致软件兼容报错
========================================== 博士同学最近联系我,说是自己的mac系统升级后之前可以用的网络软件不可用使用了,由于平时工作需要,这个网络软件如果 ...