Server‑naar‑server‑integriteit beschermen met HMAC‑handtekeningen in Django/DRF
Wanneer servers met elkaar communiceren, hoe kunnen we dan zeker weten dat de door mij verzonden aanvraag echt is en niet onderweg is gewijzigd? In dit artikel leg ik uit hoe je in een Django/DRF‑omgeving HMAC‑handtekeninggebaseerde authenticatie kunt gebruiken om de integriteit en betrouwbaarheid van server‑naar‑server‑aanvragen te waarborgen.
In het bijzonder:
- Wat is het doel van deze techniek?
- Welke incidenten kan het voorkomen?
- Waarom is het niet geschikt voor client‑server‑communicatie (beperkingen)?
- Hoe kun je
hmac_mixin.py+ CBV in een realistisch Django/DRF‑project opzetten?
Ik illustreer dit met codevoorbeelden.
Wat is HMAC‑handtekeningauthenticatie?
HMAC (Hash‑based Message Authentication Code) is een methode om een handtekening (signature) te genereren met behulp van een gedeeld geheim sleutel + bericht.
- Twee servers (A, B) kennen dezelfde secret key.
- Server A stuurt een
POST‑aanvraag naar server B: - Het body en een timestamp worden samengevoegd en een HMAC‑handtekening wordt berekend.
- Deze handtekening wordt in een
HTTP Headermeegezonden. - Server B berekent opnieuw de handtekening met dezelfde secret key en vergelijkt:
- Gelijk → “Dit is een legitieme aanvraag van A”.
- Verschillend → “Het is gewijzigd of vervalst”.
Kortom, HMAC‑handtekening controleert:
- Integriteit: of het bericht is gewijzigd.
- Authenticatie: of de afzender de secret key kent.
Voorwaarde: de secret key moet veilig gedeeld worden tussen de servers.
Doel en te voorkomen incidenten
1. Bescherming tegen berichtmanipulatie
Stel dat een aanvaller het verkeer onderschept:
- Hij kan het bericht iets aanpassen om een overdracht naar een andere account te forceren.
- Of hij kan parameters wijzigen voor een grotere som of andere opties.
Maar de ontvangende server berekent opnieuw de HMAC‑handtekening met het body + timestamp. Een kleine wijziging in het body leidt tot een totaal andere handtekening, waardoor de aanvraag wordt geweigerd.
2. Bescherming tegen server‑spoofing
Een aanvaller die zich voordoet als een legitieme server kan geen geldige handtekening genereren zonder de secret key. De ontvangende server detecteert dit en weigert de aanvraag.
3. Bescherming tegen replay‑aanvallen (met timestamp/nonce)
Een aanvaller kan een geldige aanvraag herhalen. Daarom voegen we vaak een timestamp (X-HMAC-Timestamp) of een nonce toe. De ontvangende server weigert aanvragen die te oud zijn of een reeds gebruikte nonce.
HMAC alleen is niet alles; HTTPS (TLS) blijft essentieel. HMAC voegt een extra laag van integriteit/authenticatie toe.
Beperkingen: waarom HMAC niet geschikt is voor client‑applicaties
“Kan niet gebruikt worden voor communicatie met een client (app, web front) vanwege reverse engineering.”
1. Secret key kan niet verborgen blijven in client‑code
- In mobiele apps, SPA‑frontend (JS) of desktop‑apps moet de key in de binaire of JavaScript‑code staan.
- Een aanvaller kan de app decompileren of de JS inspecteren en de key extraheren.
Eenmaal blootgesteld kan de aanvaller willekeurige HMAC‑handtekeningen genereren, waardoor de server niet kan onderscheiden of de aanvraag echt van de client komt.
2. HMAC is geen encryptie
HMAC is een handtekening; het versleutelt het bericht niet. Gevoelige data moeten nog steeds beschermd worden met TLS.
Wanneer gebruik je HMAC‑gebaseerde server‑naar‑server‑authenticatie? (scenario’s)
1. Django‑app → aparte DRF‑authenticatieserver
- Meerdere services gebruiken een gemeenschappelijke authenticatie/gebruikersserver (DRF).
- De hoofd‑Django‑app stuurt een
POST‑aanvraag voor login‑verificatie of token‑uitgifte. - De aanvraag wordt HMAC‑gehandteken en de DRF‑server verifieert en retourneert het resultaat.
2. Django‑app → AI‑inference‑server (DRF of FastAPI)
- Django fungeert als front‑/backend, zware AI‑inference wordt door een aparte server afgehandeld.
- Django stuurt een
POST /v1/infermet tekst, afbeelding‑URL, opties, en handtekening. - De AI‑server verifieert de handtekening en voert alleen bij geldige aanvragen inference uit.
Praktische patroon in Django/DRF: hmac_mixin.py + CBV
Een nette structuur in een project:
- Plaats
hmac_mixin.pydirect onder het project of de app. - Extraheer de gemeenschappelijke HMAC‑handtekening + POST‑logica in een mixin.
- Laat CBV‑klassen deze mixin erven.
1. hmac_mixin.py – gemeenschappelijke handtekening + POST‑methode
# hmac_mixin.py
import hmac
import hashlib
import json
import time
import requests
from django.conf import settings
class HMACRequestMixin:
# Definieer in settings.py
# Bijvoorbeeld: 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:
"""
Maak een HMAC‑handtekening met body en timestamp.
"""
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):
"""
Verstuur een HMAC‑gehandteken POST‑aanvraag naar de opgegeven URL.
"""
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. Voorbeeld van een verzendende 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):
"""
Ontvang een aanvraag van de client, stuur een HMAC‑gehandteken POST naar de interne AI‑server.
"""
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 # Bijvoorbeeld: "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)
Met dit patroon kun je eenvoudig nieuwe server‑naar‑server‑aanroepen toevoegen: erven van HMACRequestMixin en self.post_with_hmac(url, payload) aanroepen.
DRF‑ontvangende kant: HMAC‑handtekening‑authenticatieklasse
Een custom authentication‑klasse in DRF maakt het verifiëren van de handtekening eenvoudig.
# 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 # Toegestane tijdsverschil (bijv. ±60s)
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)
Toepassen in een DRF‑view
# views.py (DRF‑server)
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)
Resultaat:
- Zendende Django:
HMACRequestMixinmaakt het verzenden van HMAC‑gehandtekeningen eenvoudig. - Ontvangende DRF:
HMACSignatureAuthenticationverifieert de handtekening.
Samenvatting
- HMAC‑handtekening gebruikt een gedeelde secret key om integriteit en authenticatie van server‑naar‑server‑aanvragen te garanderen.
- Het voorkomt:
- Berichtmanipulatie
- Server‑spoofing
- Replay‑aanvallen (met timestamp/nonce)
- Het is niet geschikt voor client‑server‑communicatie omdat de key niet verborgen kan blijven en HMAC geen encryptie biedt.
- In een Django/DRF‑project kun je een
hmac_mixin.pygebruiken voor het verzenden van HMAC‑gehandtekeningen en een custom authentication‑klasse op de ontvangende kant voor verificatie.

댓글이 없습니다.