如何在Flask中用JWT-extended生成含AccessToken的API响应HMAC校验和
嘿,这个需求很靠谱——把access_token纳入HMAC计算确实能大幅提升消息完整性校验的安全性,毕竟只有持有合法token的客户端和服务端能参与这个校验逻辑。下面给你一步步拆解实现方案:
实现步骤
1. 准备安全的HMAC密钥
首先得准备一个强HMAC密钥,绝对不要硬编码到代码里,建议通过环境变量注入(比如用os.getenv读取)。密钥要足够长且随机,比如32位以上的随机字符串。
2. 获取当前请求的原始access_token
因为@jwt_required已经完成了token的合法性校验,我们可以直接从请求头中提取原始的token字符串(注意不要用解码后的payload,我们需要的是客户端发送的原始token):
from flask import request auth_header = request.headers.get('Authorization') # 理论上@jwt_required已经拦截了非法请求,这里的判断是兜底 if not auth_header or not auth_header.startswith('Bearer '): return {"error": "Invalid authorization header"}, 401 access_token = auth_header.split(' ')[1]
3. 构建待签名的标准化数据
为了保证客户端和服务端生成的哈希完全一致,必须固定JSON序列化的规则:
- 先准备基础返回数据(不要包含checksum)
- 把access_token加入到待签名的数据集里
- 按键名排序序列化,去掉多余空格
示例代码:
import json from datetime import datetime # 基础返回数据,用UTC时间避免时区差异 response_data = { "message": "important-info", "ts": datetime.utcnow().isoformat() } # 构建待签名数据(包含access_token) data_to_sign = response_data.copy() data_to_sign['access_token'] = access_token # 标准化序列化:按键排序、无多余空格 serialized_data = json.dumps(data_to_sign, sort_keys=True, separators=(',', ':'))
4. 生成HMAC校验和
用Python内置的hmac模块生成SHA256哈希(也可以根据需求换成SHA512等更强的算法):
import hmac import hashlib import os # 从环境变量读取密钥,兜底判断防止漏配置 hmac_secret = os.getenv("HMAC_SECRET_KEY") if not hmac_secret: raise ValueError("HMAC_SECRET_KEY environment variable is not set!") # 生成HMAC哈希 checksum = hmac.new( hmac_secret.encode('utf-8'), serialized_data.encode('utf-8'), hashlib.sha256 ).hexdigest()
5. 整合校验和并返回
最后把生成的checksum加入到基础返回数据中,返回给客户端:
response_data['checksum'] = checksum return response_data, 200
完整代码示例
把所有逻辑整合到你的Data类里:
from flask_jwt_extended import jwt_required from flask_restful import Resource from flask import request import json import hmac import hashlib import os from datetime import datetime class Data(Resource): @jwt_required() def get(self): """ GET Response message with HMAC integrity checksum. """ # 提取原始access_token auth_header = request.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return {"error": "Invalid authorization header"}, 401 access_token = auth_header.split(' ')[1] # 准备基础返回数据 response_data = { "message": "important-info", "ts": datetime.utcnow().isoformat() } # 构建待签名数据集 data_to_sign = response_data.copy() data_to_sign['access_token'] = access_token # 标准化序列化 serialized_data = json.dumps(data_to_sign, sort_keys=True, separators=(',', ':')) # 生成HMAC校验和 hmac_secret = os.getenv("HMAC_SECRET_KEY") if not hmac_secret: raise ValueError("HMAC_SECRET_KEY environment variable not configured") checksum = hmac.new( hmac_secret.encode('utf-8'), serialized_data.encode('utf-8'), hashlib.sha256 ).hexdigest() # 加入校验和并返回 response_data['checksum'] = checksum return response_data, 200
客户端验证逻辑
客户端拿到返回结果后,需要执行和服务端完全一致的逻辑来校验:
- 从返回结果中取出
message、ts,加上自己持有的access_token,构建相同的data_to_sign字典 - 用相同的JSON序列化规则(按键排序、无多余空格)生成序列化字符串
- 用共享的HMAC密钥和相同算法生成哈希
- 对比生成的哈希和返回的
checksum,一致则说明消息完整且未被篡改
关键注意事项
- 密钥安全:HMAC密钥必须在服务端和客户端之间安全共享,绝对不能泄露给第三方。建议用密钥管理工具(比如AWS Secrets Manager、HashiCorp Vault)存储,而不是环境变量(如果是生产环境)。
- 防止重放攻击:建议在客户端同时校验
ts的时效性,比如拒绝超过5分钟的返回结果,避免攻击者重复发送已截获的合法响应。 - 序列化一致性:客户端和服务端的JSON序列化规则必须完全一致(包括键排序、分隔符、日期格式),哪怕是一个空格的差异都会导致哈希不匹配。
- token有效性:客户端在验证checksum前,要确保自己的access_token未过期且合法,避免用过期token生成的哈希和服务端不一致。
内容的提问来源于stack exchange,提问作者Daedalus
相关产品推荐
相关产品推荐

