揭秘JWT:从CTF实战到Web开发,py使用JWT令牌验证

揭秘JWT:从CTF实战到Web开发,使用JWT令牌验证

介绍

JWT(JSON Web Tokens)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在网络上安全地传输信息。这种信息可以验证和信任,因为它是数字签名的。JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。

「优点」

  • 「无状态」:服务器不需要保存会话信息,减轻了服务器负担。

  • 「可扩展性」:易于在分布式系统中使用,支持跨域身份验证。

  • 「安全性」:通过数字签名确保信息的完整性和来源可信。

「缺点」

  • 「令牌大小」:由于包含头部、负载和签名,JWT的大小可能相对较大。

  • 「性能」:每次请求都需要验证JWT,可能会增加服务器的处理时间。

  • 「敏感信息泄露」:虽然Payload部分可以加密,但如果不当处理,仍可能泄露敏感信息。

简单复现

CTFShow-web345

image-20240703171126995

image-20240703171126995

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

image-20240703171143135

image-20240703171143135

抓包

image-20240703171355547

image-20240703171355547

修改请求头为admin/index.php

image-20240703171446354

image-20240703171446354

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

image-20240703171626103

image-20240703171626103

第二段就是payload,元数据

image-20240703171653730

image-20240703171653730

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

image-20240703171915498

image-20240703171915498

302重定向了

image-20240703171930648

image-20240703171930648

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

image-20240703172045891

image-20240703172045891

成功了

image-20240703172059972

image-20240703172059972

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  --------------------------- --------------- 校验字段--------------------

解密后

image-20240703100522276

image-20240703100522276

将各段尝试使用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校验抛出的异常类基本都在jwtjwt.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

image-20240703145540255

image-20240703145540255

image-20240703144709304

image-20240703144709304

image-20240703144735604

image-20240703144735604

image-20240703144802164

image-20240703144802164

image-20240703144813369

image-20240703144813369

可以发现这个基于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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/767791.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Eclipse运行main函数报 launch error

右键run as java application&#xff0c;运行main函数的时候报launch error 解决方式&#xff1a;文件右键run configurations 旧的是Project JRE&#xff0c;改成下图这个样子

【Java学习笔记】java图形界面编程

在前面的章节中&#xff0c;我们开发运行的应用程序都没有图形界面&#xff0c;但是很多应用软件&#xff0c;如Windows下的Office办公软件、扑克牌接龙游戏软件、企业进销存ERP系统等&#xff0c;都有很漂亮的图形界面。素以需要我们开发具有图形界面的软件。 Java图形界面编程…

Linux线程:编织并发的梦幻世界

目录 &#x1f6a9;引言 &#x1f6a9;听故事&#xff0c;引概念 &#x1f6a9;生产者消费者模型 &#x1f680;再次理解生产消费模型 &#x1f680;挖掘特点 &#x1f6a9;条件变量 &#x1f680;条件变量常用接口 &#x1f680;条件变量的原理 &#x1f6a9;引言 上一篇…

学习LLM的随笔

1、信息量、信息熵、交叉熵和困惑度 &#xff08;1&#xff09;信息熵&#xff1a;信息熵中使用 l o g 2 ( p ( x ) ) log_2(p(x)) log2​(p(x)) 来表示对 x x x 编码需要的编码长度。由于不同事件发生的概率不同&#xff0c;我们不能简单地将这些信息量相加&#xff0c;而应…

Day01-02-gitlab

Day01-02-gitlab 1. 什么是gitlab2. Gitlab vs Github/Gitee3. Gitlab 应用场景4. 架构5. Gitlab 快速上手指南5.0 安装要求5.1 安装Gitlab组件5.3 配置访问url5.6 初始化5.8 登录与查看5.9 汉化5.10 设置密码5.11 目录结构5.12 删除5.13 500 vs 5025.14 重置密码 6. Gitlab用户…

springboot美术馆售票管理系统-计算机毕业设计源码17485

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 2.2.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

PMP报考条件是什么?很多人都没读懂...

最近正值8月份考试报名期&#xff0c;想计划考8月份考试的宝子可以准备起来了&#xff0c;下面是报名时间和考试安排 8月考试时间安排&#xff1a; &#x1f449;报名时间在7.9日—12日 &#x1f449;考试时间在8.31日&#xff08;周六&#xff09; 一、PMP报名条件是什么&am…

vue3中 slot使用

默认插槽&#xff1a; 这是最基本的插槽类型&#xff0c;当没有指定 name 属性时&#xff0c;插槽是默认插槽。 子组件&#xff1a; <template><div class"child"><h2>子组件内容</h2><slot></slot> <!-- 默认插槽&#x…

反射快速入门

反射就是通过字节码文件获取类的成员变量、构造方法和成员方法的所有信息。 利用反射&#xff0c;我们可以获取成员变量的修饰符、名字、类型、取值。我们可以获取构造方法的名字、形参&#xff0c;并利用通过反射获取的构造方法创建对象。我们可以获取成员方法的修饰符、名字、…

LeetCode 子集

原题链接78. 子集 - 力扣&#xff08;LeetCode&#xff09; 这是一道暴力搜索问题参考大佬们的题解&#xff0c;对这类题目做出一下总结 1.确定递归参数变量 2.递归结束条件 3.做出选择&#xff0c;递归调用进入下一层 4.回溯&#xff0c;返回到递归前的状态 要完成前面这…

2024Datawhale-AI夏令营——机器学习挑战赛——学习笔记

#ai夏令营#datawhale#夏令营 Day1:入门级demo运行 这个其实比较简单&#xff0c;按照操作来做就行了&#xff0c;特征工程和调参暂时都没有做&#xff0c;后续的才是重头戏。

你想活出怎样的人生?

hi~好久不见&#xff0c;距离上次发文隔了有段时间了&#xff0c;这段时间&#xff0c;我是裸辞去感受了一下前端市场的水深火热&#xff0c;那么这次咱们不聊技术&#xff0c;就说一说最近这段时间的经历和一些感触吧。 先说一下自己的个人情况&#xff0c;目前做前端四年&am…

时钟服务器方案选型推荐:ATGM332D-5T和ATGM331C-5T

ATGM331C-5T系列模块同样是具有高灵敏度、低功耗、低成本等优势&#xff0c;适用于电力授时设备、时钟服务器、守时设备&#xff0c;可以直接替换Ublox LEA T系列模块。 性能指标&#xff1a; 从下面的图来看&#xff0c;ATGM331C-5T系列比ATGM332D-5T系列性能更好&#xff0c;…

大模型对汽车行业意味着什么?_汽车企业大模型

引 言 大模型是一种利用海量数据进行训练的深度神经网络模型&#xff0c;其特点是拥有庞大的参数规模和复杂的计算结构。通过在大规模数据集上进行训练&#xff0c;大模型能够学习到丰富的模式和特征&#xff0c;从而具备强大的泛化能力&#xff0c;可以对未知数据做出准确的预…

轻松省电!教你苹果手机自动调节亮度怎么设置

在日常使用手机的过程中&#xff0c;屏幕亮度是影响电池续航的关键因素之一。苹果手机提供的自动调节亮度功能&#xff0c;可以根据环境光线自动调整屏幕亮度&#xff0c;从而提供最佳的视觉体验并有效延长电池使用时间。想知道苹果手机自动调节亮度怎么设置吗&#xff1f; 本…

如何使用巴比达免费内网穿透服务

#灵感# 访问内部网络资源变得越来越重要。无论是远程办公、远程监控设备还是远程管理服务器&#xff0c;内网穿透技术都提供了一种安全、便捷的解决方案。巴比达&#xff08;Babada&#xff09;作为一种免费的内网穿透服务&#xff0c;为用户提供了快速、安全地访问其内部网络中…

AI人才争夺战:巨头眼中的产品经理必备技能

前言 在人工智能的浪潮下&#xff0c;BAT等一线互联网企业纷纷加码布局&#xff0c;对AI领域的人才需求空前高涨。然而&#xff0c;要在众多求职者中脱颖而出&#xff0c;成为企业眼中的人才&#xff0c;不仅需要深厚的产品功底&#xff0c;更要具备对AI的深刻理解和应用能力。…

JAVASE进阶day03(lamda表达式 ,内部类)

内部类 1.内部类的基本使用 package com.lu.day03;public class Student {private int b 12;public class A{private int b 11;public void show(){int b 10;System.out.println("我是A");System.out.println(b);System.out.println(this.b);System.out.println(…

开源即正义,3D软件Blender设计指南

在当今数字化时代&#xff0c;开源软件的崛起不仅代表着技术的发展&#xff0c;更象征着一种信息自由和技术民主的理念。其本质是集众人之智&#xff0c;共同去完善一个软件&#xff0c;最终使双方互惠共赢。具体来说&#xff0c;开源的价值&#xff0c;在于打破资源垄断&#…

PIN对PIN替代T J A 1 0 2 8,LIN芯片

内部集成高压 LDO&#xff08;耐压 40V&#xff09; 稳压源&#xff0c;可为外部 ECU微控制器或相关外设提供稳定的 5V/3.3V 电源&#xff0c;输出电流可达70mA&#xff1b; 可在 5.5V ~ 28V 电压范围内工作&#xff0c;支持 12V 应用。在休眠模式下可实现极低电流消耗&#x…