Saltar al contingut principal

Verificació de signatura de webhooks

Cada petició de webhook de l'API sanitària de vitalera inclou una signatura criptogràfica perquè pugueu verificar que la càrrega útil és autèntica i no ha estat manipulada. Això és essencial per a integracions de monitorització remota de pacients on les càrregues útils de webhooks contenen dades clíniques sensibles com constants vitals de pacients, activacions d'alarmes i canvis de consentiment.

Com funciona

  1. Quan configureu un punt d'accés de webhook, establiu una clau secreta a la configuració de la vostra organització.
  2. vitalera calcula una signatura HMAC-SHA256 del cos brut de la petició utilitzant la vostra clau secreta.
  3. La signatura s'inclou a la capçalera HTTP x-webhook-humanai-signature.
  4. La vostra aplicació recalcula la signatura i la compara amb el valor de la capçalera.

Passos de verificació

  1. Extraieu la capçalera x-webhook-humanai-signature de la petició entrant.
  2. Llegiu el cos brut de la petició com a bytes (abans de qualsevol anàlisi JSON).
  3. Calculeu el resum HMAC-SHA256 del cos brut utilitzant el vostre secret de webhook.
  4. Compareu la signatura calculada amb el valor de la capçalera utilitzant una funció de comparació de temps constant per prevenir atacs de temporització.
  5. Si les signatures coincideixen, la càrrega útil és autèntica. Si no, rebutgeu la petició.

Exemples de codi

Python

import hmac
import hashlib

def verify_webhook(request_body: bytes, signature_header: str, secret: str) -> bool:
expected_signature = hmac.new(
key=secret.encode("utf-8"),
msg=request_body,
digestmod=hashlib.sha256,
).hexdigest()

return hmac.compare_digest(expected_signature, signature_header)


# Usage in a Flask endpoint
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = "your-webhook-secret"

@app.route("/webhooks/vitalera", methods=["POST"])
def handle_webhook():
signature = request.headers.get("x-webhook-humanai-signature", "")
if not verify_webhook(request.data, signature, WEBHOOK_SECRET):
abort(401, "Invalid signature")

payload = request.get_json()
event_type = payload["event_type"]

# Process the event
print(f"Received event: {event_type}")
return "", 200

Node.js

const crypto = require('crypto');

function verifyWebhook(rawBody, signatureHeader, secret) {
const expectedSignature = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');

return crypto.timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(signatureHeader));
}

// Usage in an Express endpoint
const express = require('express');
const app = express();

const WEBHOOK_SECRET = 'your-webhook-secret';

app.post('/webhooks/vitalera', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-humanai-signature'] || '';

if (!verifyWebhook(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

const payload = JSON.parse(req.body);
console.log(`Received event: ${payload.event_type}`);
res.sendStatus(200);
});

app.listen(3000);

Bones pràctiques de seguretat

  • Verifiqueu sempre les signatures abans de processar dades de webhook. Mai confieu en càrregues útils no verificades.
  • Utilitzeu comparació de temps constant (hmac.compare_digest a Python, crypto.timingSafeEqual a Node.js) per prevenir atacs de temporització.
  • Llegiu el cos brut abans de l'anàlisi JSON. Analitzar i re-serialitzar JSON pot canviar espais en blanc o l'ordre de les claus, invalidant la signatura.
  • Utilitzeu punts d'accés HTTPS per protegir les càrregues útils de webhooks en trànsit.
  • Roteu els secrets periòdicament i actualitzeu la configuració del vostre webhook quan ho feu.
  • Rebutgeu peticions repetides comprovant el camp timestamp a la càrrega útil i descartant esdeveniments anteriors a una finestra raonable (p. ex., 5 minuts).

Resolució de problemes

ProblemaSolució
Desajust de signaturaAssegureu-vos que esteu fent el hash dels bytes bruts del cos de la petició, no una versió analitzada/re-serialitzada.
Capçalera de signatura buidaComproveu que la configuració del vostre webhook inclou una clau secreta.
Fallades de verificació intermitentsAssegureu-vos que el vostre framework web no estigui modificant el cos de la petició abans de llegir-lo.

Passos següents