在 Django/DRF 中使用 HMAC 签名保障服务器间请求完整性
当服务器与服务器通信时,如何确认“这确实是我发出的请求”、“中间没有被篡改”?本文整理了在 Django / DRF 环境中使用 HMAC 签名认证,确保服务器间请求的完整性与可信度的方法。
特别是,本文将说明:
- 该技术的目的
- 能防止哪些安全事件
- 为什么 客户端-服务器通信不适合使用(局限性)
- 在实际 Django/DRF 项目中如何通过
hmac_mixin.py+ CBV 进行实现
并配合代码示例进行说明。
HMAC 签名认证是什么?
HMAC(Hash-based Message Authentication Code) 是一种
通过 共享的秘密密钥 + 消息 生成 签名值(signature) 的方式
。
- 两台服务器(A、B)假设都知道同一个 secret key。
-
A 服务器向 B 服务器发起
POST请求时: -
将请求 正文(body) 与 时间戳(timestamp) 等合并,使用 HMAC 生成签名值
- 将该签名值放在
HTTP Header等位置一起发送 -
B 服务器使用同一 secret key 再次计算签名:
-
若值相同 = “这是 A 正常发来的请求”
- 若值不同 = “中间被篡改或有人伪造请求”
换句话说,HMAC 签名可以同时验证:
- 完整性(integrity):内容是否被篡改
- 身份验证(authentication):是否由持有秘密密钥的一方发出
※ 前提:仅在服务器间能够安全共享秘密密钥。
该技术的目的与可防止的安全事件
1. 防止请求正文被篡改
假设攻击者截获流量,尝试
- 将请求正文稍作修改,转账到其他账户
- 改变参数,提升金额或更改选项
但接收服务器会使用“请求正文 + 时间戳”重新计算 HMAC 签名。
- 正文即使改动一字,签名也会完全不同
- 签名不匹配时,服务器可直接拒绝请求
2. 防止服务器伪装(spoofing)
攻击者若尝试“冒充服务器”发送请求,
- 若不知道秘密密钥,无法生成正确签名
- 接收服务器会判定为“签名验证失败 → 不是合法服务器”
3. 防止重放攻击(replay attack)
攻击者可能复制之前有效的请求多次发送。
因此 HMAC 签名通常会包含:
- 时间戳(如
X-HMAC-Timestamp) - 或 一次性 nonce
接收服务器可实施:
- “请求时间过旧则拒绝”
- “已处理过的 nonce 不再接受”
※ 当然,HMAC 不是万能的,必须在 HTTPS(TLS)之上使用,它只是额外的完整性/身份验证层。
HMAC 签名的局限:为什么不适合客户端(App、Web 前端)
“在客户端与服务器通信时不适用(逆向工程导致密钥泄露)”
1. 客户端代码无法隐藏秘密密钥
- 移动 App、SPA 前端(JS)、桌面 App 等客户端代码最终会被打包或在浏览器中运行
- 攻击者可反编译或查看 JS,直接提取 secret key
一旦密钥泄露:
- 攻击者可随意生成合法 HMAC,伪造请求
- 服务器无法区分“真实客户端”与“伪造请求”
2. HMAC 不是加密(正文仍可被窃听)
HMAC 只验证“内容是否被篡改”,并不隐藏正文。
- 仍需通过 TLS(HTTPS)保护敏感数据
- 需要使用 JWT、OAuth2、Session 等常规 API 认证方式
结论:HMAC 适用于 服务器 ↔ 服务器 或 后端 ↔ 后端微服务 的可信环境;不适用于已公开的客户端。
何时使用 HMAC 基于服务器间认证?(场景)
以下情况特别适合使用 HMAC 签名:
1. Django 应用 → 独立 DRF 认证服务器
- 多个服务共用一个 认证/用户服务器(DRF)
- 主 Django 应用需要向 DRF 服务器发起
POST(登录校验、令牌颁发等)
此时:
- Django 通过 HMAC 签名请求
- DRF 服务器验证签名后返回结果
2. Django 应用 → AI 推理服务器(DRF 或 FastAPI 等)
- Django 负责前后端,AI 推理任务交给独立服务器
- Django 向 AI 服务器发送
POST /v1/infer,携带文本、图片 URL、选项等 - AI 服务器验证 HMAC 后才使用 GPU 进行推理
这样,即使内部网络,也能为服务间请求添加额外的可信层。
Django/DRF 实战模式:hmac_mixin.py + CBV
在实际项目中,以下结构非常简洁。
- 在项目(或 app)根目录放置
hmac_mixin.py - 把 通用 HMAC 签名 + POST 逻辑 提取为 Mixin
- 需要
POST的 CBV 继承该 Mixin
下面给出示例。
1. hmac_mixin.py – 通用签名 + POST 方法
# hmac_mixin.py
import hmac
import hashlib
import json
import time
import requests
from django.conf import settings
class HMACRequestMixin:
# 在 settings.py 中定义
# 例:HMAC_SECRET_KEY = "super-secret-key-from-env"
HMAC_SECRET_KEY = settings.HMAC_SECRET_KEY.encode("utf-8")
HMAC_HEADER_NAME = "X-HMAC-Signature"
HMAC_TIMESTAMP_HEADER = "X-HMAC-Timestamp"
HMAC_ALGORITHM = hashlib.sha256
def build_hmac_signature(self, body: bytes, timestamp: str) -> str:
"""
用 body 与 timestamp 生成 HMAC 签名。
"""
message = timestamp.encode("utf-8") + b"." + body
digest = hmac.new(self.HMAC_SECRET_KEY, message, self.HMAC_ALGORITHM).hexdigest()
return digest
def post_with_hmac(self, url: str, payload: dict, timeout: int = 5):
"""
向给定 URL 发送 HMAC 签名的 POST 请求。
"""
body = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
timestamp = str(int(time.time()))
signature = self.build_hmac_signature(body, timestamp)
headers = {
"Content-Type": "application/json",
self.HMAC_HEADER_NAME: signature,
self.HMAC_TIMESTAMP_HEADER: timestamp,
}
response = requests.post(url, data=body, headers=headers, timeout=timeout)
response.raise_for_status()
return response
只要任何 CBV 继承 HMACRequestMixin,就能轻松发送 HMAC 签名请求。
2. 发送端 Django CBV 示例
# views.py
from django.conf import settings
from django.http import JsonResponse
from django.views import View
from .hmac_mixin import HMACRequestMixin
class AIInferenceRequestView(HMACRequestMixin, View):
"""
接收客户端请求后,向内部 AI 服务器发送 HMAC 签名的 POST。
"""
def post(self, request, *args, **kwargs):
data = json.loads(request.body.decode("utf-8"))
payload = {
"text": data.get("text", ""),
"user_id": request.user.id if request.user.is_authenticated else None,
}
url = settings.AI_SERVER_URL # 例:"https://ai-service.internal/v1/infer"
try:
ai_response = self.post_with_hmac(url, payload)
except requests.RequestException as e:
return JsonResponse({"detail": "AI server error", "error": str(e)}, status=502)
return JsonResponse(ai_response.json(), status=ai_response.status_code)
这样,只需在需要的 View 继承 HMACRequestMixin 并调用 self.post_with_hmac(url, payload) 即可。
DRF 接收端:HMAC 签名验证 Authentication 类示例
接收方(DRF 服务器)需要验证 HMAC。最常见做法是自定义 Authentication 类。
# authentication.py
import hmac
import hashlib
import time
from django.conf import settings
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class HMACSignatureAuthentication(BaseAuthentication):
SECRET_KEY = settings.HMAC_SECRET_KEY.encode("utf-8")
HMAC_HEADER_NAME = "X-HMAC-Signature"
HMAC_TIMESTAMP_HEADER = "X-HMAC-Timestamp"
ALGORITHM = hashlib.sha256
MAX_SKEW_SECONDS = 60 # 允许时间偏差(±60 秒)
def _build_signature(self, body: bytes, timestamp: str) -> str:
message = timestamp.encode("utf-8") + b"." + body
return hmac.new(self.SECRET_KEY, message, self.ALGORITHM).hexdigest()
def authenticate(self, request):
signature = request.headers.get(self.HMAC_HEADER_NAME)
timestamp = request.headers.get(self.HMAC_TIMESTAMP_HEADER)
if not signature or not timestamp:
raise AuthenticationFailed("Missing HMAC headers")
try:
ts = int(timestamp)
except ValueError:
raise AuthenticationFailed("Invalid timestamp")
now = int(time.time())
if abs(now - ts) > self.MAX_SKEW_SECONDS:
raise AuthenticationFailed("Request timestamp too old")
expected_signature = self._build_signature(request.body, timestamp)
if not hmac.compare_digest(signature, expected_signature):
raise AuthenticationFailed("Invalid HMAC signature")
return (None, None)
在 DRF View 中使用
# views.py (DRF 服务器侧)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from .authentication import HMACSignatureAuthentication
class AIInferenceView(APIView):
authentication_classes = [HMACSignatureAuthentication]
permission_classes = [AllowAny] # 仅凭 HMAC 保护
def post(self, request, *args, **kwargs):
input_text = request.data.get("text", "")
result = {"answer": f"AI result for: {input_text}"}
return Response(result)
这样即可实现:
- 发送端 Django:使用
HMACRequestMixin发送签名请求 - 接收端 DRF:使用
HMACSignatureAuthentication验证签名
小结
- HMAC 签名利用共享秘密密钥,同时验证请求的完整性与身份。
- 它能有效防止:
- 请求正文被篡改
- 服务器伪装
- 重放攻击(配合时间戳/nonce)
- 但 不适合客户端,因为密钥无法隐藏,且 HMAC 不是加密。
- 在 Django/DRF 项目中,推荐使用
hmac_mixin.py提取通用签名逻辑,并在 DRF 端通过自定义 Authentication 类完成验证。

目前没有评论。