Assurer l’intégrité des requêtes serveur‑vers‑serveur avec la signature HMAC dans Django/DRF
Lorsque deux serveurs communiquent, comment vérifier que la requête que j’ai envoyée est bien celle que j’ai envoyée et qu’elle n’a pas été modifiée en transit ? Dans cet article, nous expliquons comment utiliser l’authentification basée sur la signature HMAC pour garantir l’intégrité et la fiabilité des requêtes entre serveurs dans un environnement Django / DRF.
Plus précisément, nous aborderons :
- Le but de cette technique
- Les incidents qu’elle permet d’éviter
- Pourquoi elle n’est pas adaptée aux communications client‑serveur (limitations)
- Comment l’implémenter dans un projet Django/DRF avec
hmac_mixin.pyet les CBV
Qu’est‑ce qu’une authentification par signature HMAC ?
HMAC (Hash‑based Message Authentication Code) est une méthode qui
combine une clé secrète partagée et un message pour produire une valeur de signature.
- Deux serveurs (A et B) partagent une même clé secrète.
- Quand le serveur A envoie une requête
POSTau serveur B : - Il concatène le corps de la requête et un horodatage pour créer la signature HMAC.
- Il transmet cette signature dans un en‑tête HTTP (ex.
X-HMAC-Signature). - Le serveur B calcule lui‑même la signature avec la même clé secrète et compare :
- Si les signatures correspondent, la requête est authentique.
- Sinon, elle est rejetée.
En résumé, la signature HMAC vérifie simultanément :
- Intégrité – le contenu n’a pas été altéré.
- Authentification – la requête provient d’un serveur qui connaît la clé secrète.
⚠️ Prérequis : la clé secrète doit pouvoir être partagée en toute sécurité uniquement entre serveurs.
Objectifs et incidents évités
1. Empêcher la falsification du corps de la requête
Si un attaquant intercepte le trafic, il ne peut pas modifier le corps sans que la signature ne change. Même un seul caractère suffit à invalider la requête.
2. Empêcher le spoofing de serveur
Sans la clé secrète, un attaquant ne peut pas produire une signature valide. Le serveur récepteur rejette alors la requête.
3. Empêcher les attaques par rejeu (replay)
En incluant un horodatage ou un nonce dans la signature, le serveur peut rejeter les requêtes trop anciennes ou déjà traitées.
Remarque : HMAC doit être utilisé en complément de HTTPS/TLS, qui assure la confidentialité.
Limites : pourquoi ne pas l’utiliser côté client (app, web) ?
1. Impossible de cacher la clé secrète dans le code client
Les applications mobiles, les SPA JavaScript ou les applications de bureau doivent embarquer la clé dans le binaire ou le code source. Un attaquant peut alors la extraire.
Une exposition de la clé rend la signature inutilisable : l’attaquant peut alors générer des requêtes authentifiées.
2. HMAC n’est pas un chiffrement
Il ne masque pas le contenu du corps, il ne fait que vérifier son intégrité. Les données sensibles doivent toujours être protégées par TLS.
Quand utiliser une authentification HMAC serveur‑vers‑serveur ?
1. Application Django → serveur d’authentification DRF
Une architecture où plusieurs services partagent un serveur d’authentification DRF. Le serveur Django envoie des requêtes POST signées HMAC au serveur DRF pour valider les connexions ou délivrer des tokens.
2. Application Django → serveur d’inférence AI (DRF, FastAPI, etc.)
Le serveur Django agit comme façade, tandis que les tâches lourdes d’inférence sont déléguées à un serveur AI distinct. Les requêtes POST /v1/infer sont signées HMAC pour garantir qu’elles proviennent bien du serveur Django.
Pattern pratique dans Django/DRF : hmac_mixin.py + CBV
Un projet typique organise le code ainsi :
- Créez
hmac_mixin.pyà la racine du projet ou de l’app. - Extrait la logique commune de signature HMAC et d’envoi de requêtes
POSTdans un mixin. - Les CBV qui doivent envoyer des requêtes signées héritent de ce mixin.
1. hmac_mixin.py – signature + méthode post_with_hmac
# hmac_mixin.py
import hmac
import hashlib
import json
import time
import requests
from django.conf import settings
class HMACRequestMixin:
# Définir dans 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:
"""Créer la signature HMAC à partir du corps et de l’horodatage."""
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):
"""Envoyer une requête POST signée HMAC vers l’URL donnée."""
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
2. Exemple de CBV émetteur
# 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):
"""Reçoit une requête client, la transmet au serveur AI avec HMAC."""
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 # ex. "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)
Authentification HMAC côté serveur DRF
1. Classe d’authentification personnalisée
# 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
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)
2. Application dans une vue DRF
# views.py (serveur 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]
def post(self, request, *args, **kwargs):
input_text = request.data.get("text", "")
result = {"answer": f"AI result for: {input_text}"}
return Response(result)
Résumé
- La signature HMAC permet de vérifier l’intégrité et l’authenticité des requêtes entre serveurs grâce à une clé secrète partagée.
- Elle protège contre la falsification du corps, le spoofing de serveur et les attaques par rejeu lorsqu’elle est combinée à un horodatage ou un nonce.
- Elle n’est pas adaptée aux clients (app, JS) car la clé ne peut pas être cachée.
- Dans un projet Django/DRF, un mixin
HMACRequestMixinsimplifie l’envoi de requêtes signées, tandis qu’une classe d’authentification personnaliséeHMACSignatureAuthenticationvérifie la signature côté serveur.

Aucun commentaire.