API y Webhooks de Aona

Lea sus eventos de seguridad a través de REST y recíbalos como webhooks firmados. Ambas superficies utilizan el formato OCSF (Open Cybersecurity Schema Framework) para que Sentinel, Splunk y Amazon Security Lake puedan ingerirlos de forma nativa. Los campos específicos de Aona se conservan en enrichments[].

Inicio rápido

Cree una clave en el panel en /api-keys con el alcance events:read y luego:

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

La respuesta está paginada por keyset: pase el next_cursor devuelto como ?cursor= en la siguiente solicitud. Los cursores son opacos y firmados, no los construya a mano.

Verificar webhooks, Node.js

Aona firma cada webhook con HMAC-SHA256. Verifique antes de analizar el cuerpo 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;
}

Verificar 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

Rotación de secretos (sin tiempo de inactividad)

Cuando rota el secreto de firma de un webhook en el panel, el secreto anterior sigue siendo válido durante 24 horas. Durante esa ventana, ambos secretos se verifican correctamente. El patrón recomendado se muestra en el código de verificación anterior: pruebe primero el secreto actual y luego recurra al anterior si lo tiene. Actualice su entorno con el nuevo secreto y retire el anterior cuando le convenga.

Protección contra reenvíos

El componente t= de X-Aona-Signature es la marca de tiempo en segundos unix en la que firmamos la carga útil. El verificador rechaza las entregas en las que |now - t| &gt; toleranceSeconds (5 minutos por defecto). No lo relaje sin motivo, es lo único que le protege de que alguien reenvíe una carga útil interceptada.

Idempotencia

Cada entrega incluye X-Aona-Delivery: &lt;uuid&gt;. Si una entrega supera el tiempo de espera y la reintentamos, enviamos el mismo identificador. Deduplique según él para una garantía de «al menos una vez», los receptores que lo almacenan en una pequeña caché acotada nunca actuarán dos veces sobre el mismo evento.

Referencia de eventos OCSF

Los eventos de Aona se ajustan a OCSF class_uid 6005 (Application Activity / Web Resources Activity). Los campos propios de Aona aparecen dentro de enrichments[] con tipos explícitos.

{
  "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" }
  ]
}

Para el esquema completo, consulte la especificación OpenAPI (JSON legible por máquina en /v1/docs-json).

Códigos de estado y límites de tasa

Notas de seguridad