API et Webhooks Aona

Lisez vos événements de sécurité via REST et recevez-les sous forme de webhooks signés. Les deux surfaces utilisent le format OCSF (Open Cybersecurity Schema Framework) afin que Sentinel, Splunk et Amazon Security Lake puissent les ingérer nativement. Les champs propres à Aona sont conservés dans enrichments[].

Démarrage rapide

Créez une clé dans le tableau de bord à l'adresse /api-keys avec la portée events:read, puis :

curl -H "X-Api-Key: aona_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
     https://api.aona.ai/v1/events

La réponse est paginée par keyset : transmettez le next_cursor retourné dans ?cursor= à la requête suivante. Les curseurs sont opaques et signés, ne les construisez pas à la main.

Vérifier les webhooks, Node.js

Aona signe chaque webhook avec HMAC-SHA256. Vérifiez avant d'analyser le corps JSON :

import { createHmac, timingSafeEqual } from "node:crypto";

/**
 * Verify an Aona webhook delivery.
 *
 *   const ok = verifyAonaWebhook({
 *     header: req.headers["x-aona-signature"],
 *     body: rawRequestBody,         // string, before JSON.parse
 *     secret: process.env.AONA_WEBHOOK_SECRET,
 *     previousSecret: process.env.AONA_WEBHOOK_SECRET_PREVIOUS, // optional during rotation
 *     toleranceSeconds: 300,
 *   });
 */
export function verifyAonaWebhook({
  header,
  body,
  secret,
  previousSecret,
  toleranceSeconds = 300,
}) {
  if (typeof header !== "string") return false;

  // Parse "t=<unix>,v1=<hex>"
  let t = null, v1 = null;
  for (const part of header.split(",")) {
    const [k, v] = part.split("=");
    if (k === "t") t = Number(v);
    else if (k === "v1") v1 = v;
  }
  if (!t || !v1 || !/^[0-9a-f]{64}$/i.test(v1)) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSeconds) return false;

  const payload = `${t}.${body}`;
  const tryVerify = (s) => {
    const expected = createHmac("sha256", s).update(payload).digest("hex");
    try {
      return timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expected, "hex"));
    } catch {
      return false;
    }
  };

  if (tryVerify(secret)) return true;
  if (previousSecret && tryVerify(previousSecret)) return true;
  return false;
}

Vérifier les webhooks, Python

import hmac
import hashlib
import time


def verify_aona_webhook(header: str, body: str, secret: str,
                       previous_secret: str | None = None,
                       tolerance_seconds: int = 300) -> bool:
    """Verify an Aona webhook delivery.

    Pass the raw request body (bytes-or-string before JSON.parse) — the
    signature is computed over the wire payload, not the parsed dict.
    """
    if not isinstance(header, str):
        return False

    parsed = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
    try:
        t = int(parsed["t"])
        v1 = parsed["v1"]
    except (KeyError, ValueError):
        return False
    if not (len(v1) == 64 and all(c in "0123456789abcdefABCDEF" for c in v1)):
        return False
    if abs(int(time.time()) - t) > tolerance_seconds:
        return False

    payload = f"{t}.{body}".encode("utf-8")
    def _try(s: str) -> bool:
        expected = hmac.new(s.encode("utf-8"), payload, hashlib.sha256).hexdigest()
        return hmac.compare_digest(v1.lower(), expected.lower())

    if _try(secret):
        return True
    if previous_secret and _try(previous_secret):
        return True
    return False

Rotation du secret (sans interruption)

Lorsque vous faites tourner le secret de signature d'un webhook dans le tableau de bord, le secret précédent reste valide pendant 24 heures. Durant cette fenêtre, les deux secrets sont vérifiés avec succès. Le schéma recommandé figure dans le code de vérification ci-dessus : essayez d'abord le secret actuel, puis revenez au précédent si vous le possédez. Mettez à jour votre environnement avec le nouveau secret et retirez le précédent à votre convenance.

Protection contre le rejeu

Le composant t= de X-Aona-Signature est l'horodatage en secondes unix auquel nous avons signé la charge utile. Le vérificateur rejette les livraisons où |now - t| &gt; toleranceSeconds (5 minutes par défaut). N'assouplissez pas ce réglage sans raison, c'est la seule chose qui vous protège contre le rejeu d'une charge utile interceptée.

Idempotence

Chaque livraison inclut X-Aona-Delivery: &lt;uuid&gt;. Si une livraison expire et que nous réessayons, nous envoyons le même identifiant. Dédupliquez sur celui-ci pour une garantie « au moins une fois », les récepteurs qui le stockent dans un petit cache borné n'agiront jamais deux fois sur le même événement.

Référence des événements OCSF

Les événements Aona sont conformes à OCSF class_uid 6005 (Application Activity / Web Resources Activity). Les champs natifs d'Aona apparaissent dans enrichments[] avec des types explicites.

{
  "id": "01J5e2-uuid-...",
  "version": "2026-05-23",
  "class_uid": 6005,
  "category_name": "Application Activity",
  "activity_name": "Access",
  "time": "2026-05-22T14:08:12.314Z",
  "actor": { "user": { "uid": "f6c1...-uuid" } },
  "app":   { "name": "Aona", "uid": "chat-gpt-platform-uuid" },
  "type_name":   "policy_violation",
  "disposition": "BLOCK",
  "metadata": {
    "product": { "name": "Aona Workforce AI Security" },
    "tenant_uid": "your-business-uuid"
  },
  "enrichments": [
    { "name": "data_security_risk", "value": 0.83, "type": "numeric" },
    { "name": "data_privacy_risk",  "value": 0.41, "type": "numeric" },
    { "name": "policy_id", "value": "policy-uuid", "type": "uid" },
    { "name": "action",    "value": "BLOCK",       "type": "string" },
    { "name": "chat_id",   "value": "abc-123",     "type": "string" }
  ]
}

Pour le schéma complet, consultez la spécification OpenAPI (JSON exploitable par machine sur /v1/docs-json).

Codes de statut et limites de débit

Notes de sécurité