一次封装,解放双手:Requests如何实现0入侵请求与响应的智能加解密

news2025/2/23 6:49:58

引言

之前写了 Requests 自动重试的文章,突然想到,之前还用到过 Requests 自动加解密请求的逻辑,分享一下。之前在做逆向的时候,发现一般医院的小程序请求会这么玩,请求数据可能加密也可能不加密,但是返回的 json 数据是加密的,每次都要去写加解密的代码就比较麻烦,所以想了一个办法将加解密封装到 Requests 里面,下面开始。

session

session 是 Requests 中比较重要的一个类,基本上所有的请求都走 session 类的 send 方法,所以咱们这里从 send 方法入手。

服务端代码

这里为了方便演示,我开发了一个小小的 demo 服务端来演示服务端加密的场景。先看一下代码:

import base64
import json
import uuid
from hashlib import md5

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from flask import Flask, request, Response

app = Flask(__name__)


def get_nonce():
    return uuid.uuid4().__str__()


def encrypt(k, data, mode=AES.MODE_ECB):
    aes = AES.new(k.encode(), mode)
    result = aes.encrypt(pad(data.encode(), block_size=16))
    return base64.b64encode(result).decode()


def get_headers():
    return {
        'Content-Type': 'application/json',
        'x-encrypt': 'true',
        'nonce': get_nonce()
    }


@app.post('/')
def index():
    data = request.json
    print(f"get timestamp is :{request.headers.get('x-timestamp')}")
    print(f"get sign is :{request.headers.get('x-sign')}")
    print(f'json data is:{data}')
    assert md5(request.headers.get('x-timestamp').encode()).hexdigest() == request.headers.get('x-sign')
    headers = get_headers()
    data = {'encrypt': str(data['data'] * 2) + (request.headers.get('x-sign') or '')}
    print(f'before encrypt data is:{data}')
    d = encrypt(headers.get('nonce').replace('-', '')[:16], json.dumps(data))
    print(f'after encrypt data is:{d}')
    return Response(json.dumps({'data': d}), headers=headers, mimetype='application/json')


if __name__ == '__main__':
    app.run(debug=True)

这里通过 flask 写了一个接口,代码不复杂,大家应该都能看懂。简单讲下逻辑:

  • 首先这是个一个 post 请求的接口,接受 json 数据和请求头
  • 接收到请求头后获取时间戳和 sign 字段
  • 使用时间戳验证 sign 字段是否正确,不正确抛出异常,为了演示这里就不处理异常的场景了
  • 接下来处理数据,将接收到的数据取出 data 字段,复制一遍,然后添加 sign 字段后作为初始数据
  • 然后将初始数据进行加密
  • 返回加密后的数据,并且在返回的响应头中添加 nonce 字段作为解密的密钥,并且将 x-encrypt 响应头设置为 true

客户端代码

import base64
import time
from hashlib import md5

import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad


def decrypt(k, data, mode=AES.MODE_ECB):
    aes = AES.new(k.encode(), mode)
    result = aes.decrypt(base64.b64decode(data))
    return unpad(result, block_size=16).decode()


class CustomSession(requests.Session):
    def send(self, request, **kwargs):
        tt = int(time.time() * 1000)
        request.headers['x-timestamp'] = f'{tt}'
        request.headers['x-sign'] = f'{md5(str(tt).encode()).hexdigest()}'
        print(f'request.headers: {request.headers}')
        print(f'request.body: {request.body.decode()}')
        response = super().send(request, **kwargs)
        print(f'response.headers: {response.headers}')
        print(f'response.body: {response.text}')
        if response.headers.get('x-encrypt') == 'true':
            k = response.headers.get('nonce').replace('-', '')[:16]
            enc = response.json().get('data')
            print(f'encrypt data: {enc}')
            r = decrypt(k, enc, AES.MODE_ECB)
            print(f'decrypt data: {r}')
            setattr(response, '_content', r.encode())
            return response
        return response


if __name__ == '__main__':
    session = CustomSession()
    res = session.post(url='http://127.0.0.1:5000/', json={'data': 'test123'})
    print(f'response is:{res.json()}')

在客户端代码,我们继承了 session 类,并且重写了 send 方法,加入了自定义的逻辑,自定义的这块逻辑就是本篇文章的核心代码了,自定义的逻辑中总共做了几件事:

  • 首先获取了当前的时间戳,将时间戳添加到 x-timestamp 请求头中
  • 对时间戳字段取 md5 作为 sign 字段添加到 x-sign 请求头中
  • 执行正常的 send 方法逻辑
  • 判断是否存在 x-encrypt 字段并且字段的内容是否等于 true
  • 获取响应头中的 nonce 字段,并且去掉 - 后取前十六个字节作为密钥
  • 获取加密后的数据,使用上面获取到的密钥进行解密
  • 将解密后的 json 重新赋值给响应对象,返回响应

以上就是客户端和服务端的代码和执行逻辑。

当然了,这篇文章想要说的是自动加解密的逻辑,这里为了演示方便只用到了响应加密,并没有添加请求加密的逻辑,有需求的小伙伴可以自行动手添加。主要是的思路就是重写 send 方法,在 send 方法的前后,添加自定义的请求加密和响应解密的逻辑。

先看下运行结果:

服务端日志:

127.0.0.1 - - [22/Nov/2024 21:11:23] "POST / HTTP/1.1" 200 -
get timestamp is :1732281083065
get sign is :a981a12b2bdd5bd795c5323c516e2407
json data is:{'data': 'test123'}
before encrypt data is:{'encrypt': 'test123test123a981a12b2bdd5bd795c5323c516e2407'}
after encrypt data is:5qzFCwzBoTi4eA3NLW7Z8ZA/4jh5QLBRwwn8uPCYncZowxrRhqjrmQnfnC2keyN6q8Z0CrqrNej5s69A075XkA==

客户端日志:

request.headers: {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '19', 'Content-Type': 'application/json', 'x-timestamp': '1732281083065', 'x-sign': 'a981a12b2bdd5bd795c5323c516e2407'}
request.body: {"data": "test123"}
response.headers: {'Server': 'Werkzeug/3.1.3 Python/3.10.9', 'Date': 'Fri, 22 Nov 2024 13:11:23 GMT', 'Content-Type': 'application/json', 'x-encrypt': 'true', 'nonce': '3d783f6f-b0d3-43dd-8f96-14db015b8f68', 'Content-Length': '100', 'Connection': 'close'}
response.body: {"data": "5qzFCwzBoTi4eA3NLW7Z8ZA/4jh5QLBRwwn8uPCYncZowxrRhqjrmQnfnC2keyN6q8Z0CrqrNej5s69A075XkA=="}
encrypt data: 5qzFCwzBoTi4eA3NLW7Z8ZA/4jh5QLBRwwn8uPCYncZowxrRhqjrmQnfnC2keyN6q8Z0CrqrNej5s69A075XkA==
decrypt data: {"encrypt": "test123test123a981a12b2bdd5bd795c5323c516e2407"}
response is:{'encrypt': 'test123test123a981a12b2bdd5bd795c5323c516e2407'}

从客户端日志中,可以看到,请求的 body 是明文的值,请求头中包含一个时间戳字段和 sign 字段用来给服务端做校验,被发送出去后接收到的是加密后的数据,并且响应头中多了一个 nonce 字段,作为密钥用来解密响应数据,将响应数据解密后可以得到两个我们需要的响应。

从服务端日志中,可以看到,显示获取了时间戳字段和 sign 字段,并且经过处理后,将数据加密返回给客户端。

抓包验证

接下来抓包验证一下,是否如上文所说的响应是加密的,请求是明文的

响应

在这里插入图片描述

请求

image-20241122212951719

从截图中可以看到,在发送请求时,请求是明文的,在接收到响应时,响应是加密的,这里其实可以拓宽一下,如果我们将请求加密,那么整个请求和响应就都是加密的了,但是因为重写了 sessionsend 方法,其实并不会对整体的业务代码有很大的入侵,可以更加方便的更改或者判断是否需要加解密,应该如何加密和解密。这里提供了极大的灵活性,并且可以做到对业务 0 入侵。

总结

以上就是自动加解密的所有逻辑了,希望对大家有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Springboot+Vue的救灾物资调动系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

『 Linux 』网络层 - IP协议(一)

文章目录 IP协议报文格式IP协议报文如何进行报头与有效载荷分离 网段划分CIDR特殊的IP地址 IP地址的数量限制私有IP和公网IP理解运营商 IP协议报文格式 IP协议报文格式与TCP协议的报文格式类似; IP报文的宽度也是32位; 对应的IP的实际报头为20字节为定长报头(固定长度); 版本 …

[cg] vulkan external_memory

最近在写硬件编码的代码,渲染器渲染出的RT需要给到编码器做硬编,有两种方法能做。 一是通过 map的方式,把显存里的数据读到cpu,拷贝一份cpu data给编码器,但这种方式会有内存拷贝的开销。所以,我们思考是否…

springboot课程答疑系统(代码+数据库+LW)

摘要 随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应师生交流形式的网站。本文介绍了课程答疑系统的开发全过程。通过分析企业对于课程答疑系统的需求,创建了一个计算机管理课程答疑系统的方案。文章…

# issue 4 进程控制函数

目录 一、进程控制函数一 二、进程控制函数二 启动进程:(exec系列) 创建新进程: 测试代码: 测试结果: 三、进程控制函数三 结束进程: 测试代码: 测试结果: 四、…

Linux|进程程序替换

目录 什么是进程替换 替换原理 exec函数 exec* 函数的共性 什么是进程替换 进程程序替换是指将一个进程中正在运行的程序替换为另一个全新的程序的过程,但替换不是创建新进程,只是将对应程序的代码和数据进行替换。具体来说,这个替换过程涉…

2024 APMCM亚太数学建模C题 - 宠物行业及相关产业的发展分析和策略(详细解题思路)

在当下, 日益发展的时代,宠物的数量应该均为稳步上升,在美国出现了下降的趋势, 中国 2019-2020 年也下降,这部分变化可能与疫情相关。需要对该部分进行必要的解释说明。 问题 1: 基于附件 1 中的数据及您的团队收集的…

人工智能(AI)与机器学习(ML)基础知识

目录 1. 人工智能与机器学习的核心概念 什么是人工智能(AI)? 什么是机器学习(ML)? 什么是深度学习(DL)? 2. 机器学习的三大类型 (1)监督式学…

【从零开始的LeetCode-算法】3233. 统计不是特殊数字的数字数量

给你两个 正整数 l 和 r。对于任何数字 x,x 的所有正因数(除了 x 本身)被称为 x 的 真因数。 如果一个数字恰好仅有两个 真因数,则称该数字为 特殊数字。例如: 数字 4 是 特殊数字,因为它的真因数为 1 和…

web——sqliabs靶场——第十二关——(基于错误的双引号 POST 型字符型变形的注入)

判断注入类型 a OR 1 1# 发现没有报错 ,说明单引号不是闭合类型 测试别的注入条件 a) OR 1 1# a)) OR 1 1# a" OR 11 发现可以用双引号闭合 发现是")闭合 之后的流程还是与11关一样 爆破显示位 先抓包 是post传参,用hackbar来传参 unam…

IDEA 下载源码很慢,Download Source使用阿里云镜像仓库

参考: IDEA maven本地仓库、中心仓库、远程仓库配置 在观看第三方jar包的api时,有时候需要下载源码看下注释。 这个时候用idea 上的提示的Download Source会发现一直下载不下来。 因此就怀疑用的是apache的maven仓库,不是我们用的 aliyun 镜…

1+X应急响应(网络)病毒与木马的处置:

病毒与木马的处置: 病毒与木马的简介: 病毒和木马的排查与恢复:

2024年亚太地区数学建模大赛D题-探索量子加速人工智能的前沿领域

量子计算在解决复杂问题和处理大规模数据集方面具有巨大的潜力,远远超过了经典计算机的能力。当与人工智能(AI)集成时,量子计算可以带来革命性的突破。它的并行处理能力能够在更短的时间内解决更复杂的问题,这对优化和…

一个小的可编辑表格问题引起的思考

11.21工作中遇到的问题 预期:当每行获取红包金额的时候若出现错误,右侧当行会出现提示 结果:获取红包金额出现错误,右侧对应行并没有出现错误提示 我发现,当我们设置readonly的时候,其实render函数依旧是…

【Linux课程学习】:进程描述---PCB(Process Control Block)

🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 1.基本概念: 🥝进程的…

java基础概念36:正则表达式1

一、正则表达式的作用 作用一:校验字符串是否满足规则;作用二:在一段文本中查找满足要求的内容。——爬虫 二、正则表达式 2-1、字符类 示例: public static void main(String[] args) {System.out.println("a".matc…

设计模式之 命令模式

命令模式(Command Pattern)是行为型设计模式之一,它将请求(或命令)封装成一个对象,从而使用户能够将请求发送者与请求接收者解耦。通过命令模式,调用操作的对象与执行操作的对象不直接关联&…

GRU (门控循环单元 - 基于RNN - 简化LSTM又快又好 - 体现注意力的思想) + 代码实现 —— 笔记3.5《动手学深度学习》

目录 0. 前言 1. 门控隐状态 1.1 重置门和更新门 1.2 候选隐状态 1.3 隐状态 2. 从零开始实现 2.1 初始化模型参数 2.2 定义模型 2.3 训练与预测 3 简洁实现 4. 小结 0. 前言 课程全部代码(pytorch版)已上传到附件看懂上一篇RNN的所有细节&am…

实践篇:青果IP助理跨境电商的高效采集

写在前面: 近年来,跨境电商行业迅速崛起,成为全球贸易的重要组成部分。据市场调研机构Statista数据显示,2024年全球跨境电商市场规模预计将突破5万亿美元,覆盖数十亿消费者。跨境电商的竞争日益激烈,商家不…

电子应用设计方案-16:智能闹钟系统方案设计

智能闹钟系统方案设计 一、系统概述 本智能闹钟系统旨在为用户提供更加个性化、智能化和便捷的闹钟服务,帮助用户更有效地管理时间和起床。 二、系统组成 1. 微控制器 - 选用低功耗、高性能的微控制器,如 STM32 系列,负责整个系统的控制和数据…