使用AES-CBC + HMAC-SHA256实现前后端请求安全验证

news2025/5/11 3:55:02

AES-CBC + HMAC-SHA256 加密验证方案,下面是该方案二等 优点缺点 表格,适用于文档、评审或技术选型说明。

✅ 优点表格:AES-CBC + HMAC-SHA256 加密验证方案

类别优点说明
🔐 安全性使用 AES-CBC 对称加密使用 AES-128-CBC 是可靠且广泛接受的对称加密方式。
随机生成 IV每次加密生成新的 IV,有效防止密文重放与模式识别。
HMAC-SHA256 签名增强完整性校验,防止中间人篡改密文。
加密前先签名验证防止不合法密文触发解密报错或被利用。
💡 灵活性签名算法可切换支持从 HMAC-SHA256 切换为其他如 SHA-512。
密钥可由 token 派生动态生成密钥,便于用户级安全控制。
前端跨平台适用适用于 Web、小程序、移动端等多平台前端环境。
🔁 可部署性可嵌入代理层Nginx + Lua 可提前拦截无效请求,节省后端资源。
🧩 多语言兼容Node.js、Python、Lua 等实现简单支持常见语言和平台,易于团队协作与系统整合。

❌ 缺点表格:AES-CBC + HMAC 签名方案的局限

类别缺点说明
⚙️ 实现复杂度实现逻辑较多需要额外编码 IV 管理、HMAC 签名、前后端一致性等细节。
🔁 重放防护默认无时间戳或 nonce重放攻击不可防,需要自行引入 timestamp + nonce 参数。
🔑 密钥依赖密钥动态性带来兼容问题一旦 token 失效或更换,旧数据将无法解密。
📦 数据随机访问不支持局部解密AES-CBC 是块加密,不能随机访问或解密数据片段。
🕒 不适合长期缓存密文随机性增加校验复杂度每次加密结果不同,不适合用于长期静态存储的校验场景。

🧭 补充建议(可选扩展)

增强点建议
防重放在签名前加上时间戳 + nonce 字段,防止多次使用旧数据
加密模式升级可考虑迁移到 AES-GCM,天然支持认证(AEAD)
秘钥管理密钥建议动态派生(如基于用户会话、JWT claims 等)

下面是该方案的实现详细代码:


✅ 前端 JavaScript:frontend.js

// === frontend.js ===
// 前端:使用 AES-CBC 加密 + HMAC-SHA256 签名
import aesjs from 'aes-js';
import CryptoJS from 'crypto-js';

function aaa(token) {
    return aesjs.utils.utf8.toBytes(token.padEnd(16, '0').slice(0, 16));
}

function generateRandomIV() {
    let iv = new Uint8Array(16);
    window.crypto.getRandomValues(iv);
    return iv;
}

function getHmacSHA256(keyBytes, messageHex) {
    const keyHex = CryptoJS.enc.Hex.parse(aesjs.utils.hex.fromBytes(keyBytes));
    const hmac = CryptoJS.HmacSHA256(messageHex, keyHex);
    return hmac.toString(CryptoJS.enc.Hex);
}

function encryptWithMac(token, plaintext) {
    const key = aaa(token);
    const iv = generateRandomIV();
    const textBytes = aesjs.utils.utf8.toBytes(plaintext);
    const padded = aesjs.padding.pkcs7.pad(textBytes);
    const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
    const encryptedBytes = aesCbc.encrypt(padded);

    const ivHex = aesjs.utils.hex.fromBytes(iv);
    const ciphertextHex = aesjs.utils.hex.fromBytes(encryptedBytes);
    const fullDataHex = ivHex + ciphertextHex;
    const mac = getHmacSHA256(key, fullDataHex);

    return {
        data: fullDataHex,
        mac: mac
    };
}

const token = 'abc123';
const msg = 'hello world';
const result = encryptWithMac(token, msg);
console.log(JSON.stringify(result));


✅ Node.js 后端验证:backend_node.js

// backend_node.js
const crypto = require('crypto');

function deriveKey(token) {
    return Buffer.from(token.padEnd(16, '0').slice(0, 16));
}

function verifyEncryptedData(token, dataHex, macHex) {
    const key = deriveKey(token);
    const iv = Buffer.from(dataHex.slice(0, 32), 'hex');
    const ciphertext = Buffer.from(dataHex.slice(32), 'hex');

    // 验证 HMAC
    const hmac = crypto.createHmac('sha256', key);
    hmac.update(dataHex);
    const expectedMac = hmac.digest('hex');
    if (expectedMac !== macHex) {
        throw new Error('MAC 验证失败');
    }

    // 解密
    const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    decipher.setAutoPadding(true);
    let decrypted = decipher.update(ciphertext, null, 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

// 示例
const token = 'abc123';
const { data, mac } = JSON.parse(/* 前端结果 */ '{"data": "...", "mac": "..."}');

try {
    const plaintext = verifyEncryptedData(token, data, mac);
    console.log('解密成功:', plaintext);
} catch (e) {
    console.error(e.message);
}

✅ Python 后端验证:backend_python.py

# backend_python.py
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
from binascii import unhexlify

def derive_key(token: str) -> bytes:
    return token.ljust(16, '0')[:16].encode()

def verify_encrypted_data(token, data_hex, mac_hex):
    key = derive_key(token)
    iv = unhexlify(data_hex[:32])
    ciphertext = unhexlify(data_hex[32:])

    # 验证 HMAC
    h = HMAC.new(key, digestmod=SHA256)
    h.update(data_hex.encode())
    try:
        h.hexverify(mac_hex)
    except ValueError:
        raise ValueError('MAC 验证失败')

    # 解密
    cipher = AES.new(key, AES.MODE_CBC, iv)
    padded = cipher.decrypt(ciphertext)
    pad_len = padded[-1]
    return padded[:-pad_len].decode()

# 示例
token = 'abc123'
data = '...'  # 前端 data
mac = '...'   # 前端 mac

try:
    print('解密成功:', verify_encrypted_data(token, data, mac))
except Exception as e:
    print('失败:', e)

✅ Nginx + Lua (OpenResty):aes_verify.lua

-- aes_verify.lua
local aes = require "resty.aes"
local hmac = require "resty.hmac"
local str = require "resty.string"
local cjson = require "cjson"

ngx.req.read_body()
local body = ngx.req.get_body_data()
local json = cjson.decode(body)
local data = json.data
local mac = json.mac
local token = ngx.var.http_authorization:sub(8)

local key = token .. string.rep("0", 16 - #token)
key = key:sub(1, 16)

local hmac_obj = hmac:new(key, hmac.ALGOS.SHA256)
hmac_obj:update(data)
local expected_mac = str.to_hex(hmac_obj:final())

if expected_mac ~= mac then
    ngx.status = 401
    ngx.say("MAC 验证失败")
    return ngx.exit(401)
end

local iv = str.to_hex(data:sub(1, 32))
local cipher = data:sub(33)
local aes_cbc = aes:new(key, nil, aes.cipher(128, "cbc"), { iv = iv })
local decrypted = aes_cbc:decrypt(str.from_hex(cipher))
local pad = string.byte(decrypted:sub(-1))
decrypted = decrypted:sub(1, -pad-1)

ngx.say("验证通过,原文: ", decrypted)

配置片段:

location /api/secure-data {
    content_by_lua_file /etc/nginx/lua/aes_verify.lua;
    proxy_pass http://backend_service;
}

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

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

相关文章

开始使用WebStorm

目录 开始使用WebStorm打开、检出或创建项目打开项目从版本控制系统检出项目的步骤创建一个空的WebStorm项目在项目中创建新文件的步骤 熟悉WebStorm用户界面找到你要找的代码查找项目符号的调用按名称查找项目符号搜索文本片段转到符号声明历史记录 补全代码实时检查并修复代码…

【计算机视觉】Car-Plate-Detection-OpenCV-TesseractOCR:车牌检测与识别

Car-Plate-Detection-OpenCV-TesseractOCR:车牌检测与识别技术深度解析 在计算机视觉领域,车牌检测与识别(License Plate Detection and Recognition, LPDR)是一个极具实用价值的研究方向,广泛应用于智能交通系统、安…

【MongoDB篇】MongoDB的聚合框架!

目录 引言第一节:什么是聚合框架? 🤔第二节:管道的“发动机”们——常用聚合阶段详解!⚙️第三节:聚合表达式——管道中的“计算器”和“转换器” 🧮✏️第四节:性能优化与考量——让…

面试常问系列(一)-神经网络参数初始化-之自注意力机制为什么除以根号d而不是2*根号d或者3*根号d

首先先罗列几个参考文章,大家之后可以去看看,加深理解: 面试常问系列(一)-神经网络参数初始化面试常问系列(一)-神经网络参数初始化之自注意力机制_注意力机制的参数初始化怎么做-CSDN博客面试常问系列(一)-神经网络参数初始化-之-softmax-C…

C++使用PoDoFo库处理PDF文件

📚 PoDoFo 简介 PoDoFo 是一个用 C 编写的自由开源库,专用于 读取、写入和操作 PDF 文件。它适用于需要程序化处理 PDF 文件的应用程序,比如批量生成、修改、合并、提取元数据、绘图等。 🌟 核心特点 特性说明📄 P…

【Unity】Unity中修改网格的大小和倾斜网格

一、问题 unity中的网格(Grid)或者地面Plane组件,在使用时,都是正方形的网格,而且建立该网格后,在不改变Scale情况下,没发使其整体变大,而且也没法改变每个网格的大小,而…

Transformer-LSTM混合模型在时序回归中的完整流程研究

Transformer-LSTM混合模型在时序回归中的完整流程研究 引言与背景 深度学习中的长期依赖建模一直是时序预测的核心问题。长短期记忆网络(LSTM)作为一种循环神经网络,因其特殊的门控结构能够有效捕捉序列的历史信息,并在时序预测…

UE5 渲染思路笔记(角色)

参考示例 首先是怎么做到辉光只有部分有而整体没有的 使用的是Bloom内的阈值,控制光的溢光量 Threshold(阈值):这个参数决定了图像中哪些像素会参与泛光计算。只有那些亮度超过阈值的像素才会触发泛光效果。阈值越低,更多的像素会…

运维打铁:服务器分类及PHP入门

文章目录 C/S架构和B/S架构C/S架构B/S架构 服务器分类服务器类型服务器软件 使用 WampServer 搭建 HTTP服务集成环境的分类WampServer 的安装测试访问配置网站根目录 静态网站和动态网站PHP的常见语法第一段 php 代码注释变量数据类型运算符函数的定义类和对象内容输出循环语句…

Python - 爬虫;Scrapy框架(一)

框架,就相当于一个封装了很多功能的结构体,它帮我们把主要的结构给搭建好了,我们只需往骨架里添加内容就行。 Scrapy是适用于Python的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scra…

The 2024 ICPC Kunming Invitational Contest G. Be Positive

https://codeforces.com/gym/105386/problem/G 题目&#xff1a; 结论&#xff1a; 从0开始每四个相邻数的异或值为0 代码&#xff1a; #include<bits/stdc.h> using namespace std; #define int long long void solve() {int n;cin >> n;if(n1||n%40){cout &…

GET请求如何传复杂数组参数

背景 有个历史项目&#xff0c;是GET请求&#xff0c;但是很多请求还是复杂参数&#xff0c;比如&#xff1a;参数是数组&#xff0c;且数组中每一个元素都是复杂的对象&#xff0c;这个时候怎么传参数呢&#xff1f; 看之前请求直接是拼接在url后面 类似&items%5B0%5D.…

leetcode - 双指针问题

文章目录 前言 题1 移动零&#xff1a; 思路&#xff1a; 参考代码&#xff1a; 题2 复写零&#xff1a; 思考&#xff1a; 参考代码&#xff1a; 题3 快乐数&#xff1a; 思考&#xff1a; 参考代码&#xff1a; 题4 盛最多水的容器&#xff1a; 思考&#xff1a;…

人工智能之数学基础:二次型

本文重点 二次型作为线性代数领域的重要概念,架起了代数方程与几何分析之间的桥梁。从古典解析几何中的圆锥曲线方程到现代优化理论中的目标函数,二次型以其简洁的数学表达和丰富的结构特性,在数学物理、工程技术和经济金融等领域发挥着不可替代的作用。 二次型的基本概念…

【Unity笔记】实现支持不同渲染管线的天空盒曝光度控制组件(SkyboxExposureController)——参数化控制

写在前面 在Unity中&#xff0c;天空盒&#xff08;Skybox&#xff09;不仅承担视觉上的背景作用&#xff0c;更是场景环境光照与氛围塑造的重要组成部分。不同时间、天气、场景转换等&#xff0c;都需要灵活调整天空的亮度。而**曝光度&#xff08;Exposure&#xff09;**就是…

Docker 使用与部署(超详细)

目录 引入 入门使用 部署对比 镜像仓库 命令解释 基础 常见命令 示例 数据卷的使用 数据卷的概念 数据卷的使用 挂载本地目录文件 镜像 结构 Dockerfile 容器网络 部署 DockerCompose 语法 ​编辑 基础命令 引入 当我们在 Linux 上部署一个集成了很多中间件…

CSS实现图片垂直居中方法

html <div class"footer border-top-row"><div class"footer-row"><span class"footer-row-col01">制单人&#xff1a;{{ printData[pageIndex - 1].rkMaster.makerName}}<img :src"getPersonSignImgSrc(printData[pa…

Python+Scrapy跨境电商爬虫实战:从亚马逊/沃尔玛数据采集到反爬攻克(附Pangolin API高效方案)

从零实战到反爬攻克&#xff0c;揭秘跨境数据抓取全流程与Pangolin Scrape API终极方案 在当今数据驱动的跨境电商时代&#xff0c;谁掌握了优质的市场数据&#xff0c;谁就掌握了成功的关键。随着全球电商市场规模持续扩大&#xff08;据Statista最新报告显示&#xff0c;2025…

【日撸 Java 三百行】Day 7(Java的数组与矩阵元素相加)

目录 Day 7&#xff1a;Java 的数组与矩阵元素相加 一、基本知识 二、矩阵的建立与基本计算 三、代码及测试 拓展&#xff1a;Arrays类详解 小结 Day 7&#xff1a;Java 的数组与矩阵元素相加 Task&#xff1a; 矩阵的赋值.二重循环. 一、基本知识 在学习 Java 中的数组与矩…

【Python】常用命令提示符

Python常用的命令提示符 一、Python环境基础命令【Windows】 于Windows环境下&#xff0c;针对Python&#xff0c;在CMD&#xff08;命令提示符&#xff09;常用的命令以及具体用法&#xff0c;怎么用&#xff1b;   主要包含&#xff1a;运行脚本、包管理、虚拟环境、调试与…