> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whaapy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Seguridad

> Cómo verificar la autenticidad de los webhooks

# Verificación de Firma HMAC

Todos los webhooks incluyen una firma HMAC-SHA256 para verificar su autenticidad. **Siempre verifica la firma** antes de procesar un webhook.

***

## Headers de Webhook

Cada webhook incluye estos headers:

| Header                | Descripción                             |
| --------------------- | --------------------------------------- |
| `X-Webhook-Signature` | Firma HMAC-SHA256 del payload           |
| `X-Webhook-Event`     | Tipo de evento (ej: `message.received`) |
| `X-Webhook-Timestamp` | Fecha/hora ISO 8601 del envío           |
| `X-Webhook-ID`        | ID único del webhook                    |

***

## Cómo Funciona

1. Whaapy calcula un HMAC-SHA256 del payload JSON usando tu `secret`
2. El resultado se envía en el header `X-Webhook-Signature`
3. Tú calculas el mismo HMAC con el payload recibido
4. Si coinciden, el webhook es auténtico

<Warning>
  **Nunca proceses webhooks sin verificar la firma.** Un atacante podría enviar webhooks falsos a tu endpoint.
</Warning>

***

## Verificación en Node.js

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// En tu endpoint Express
app.post('/webhook', express.json(), (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const payload = req.body;
  
  if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET)) {
    console.error('Invalid webhook signature');
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Webhook verificado, procesar...
  console.log('Webhook received:', req.body.event);
  res.json({ received: true });
});
```

<Tip>
  Usa `crypto.timingSafeEqual()` para comparar firmas. Esto previene ataques de timing.
</Tip>

***

## Verificación en Python

```python theme={null}
import hmac
import hashlib
import json
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = "tu_secret_aqui"

def verify_webhook_signature(payload: dict, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        json.dumps(payload, separators=(',', ':')).encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Webhook-Signature')
    payload = request.get_json()
    
    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return jsonify({'error': 'Invalid signature'}), 401
    
    # Webhook verificado, procesar...
    print(f"Webhook received: {payload['event']}")
    return jsonify({'received': True})
```

***

## Verificación en PHP

```php theme={null}
<?php
$webhookSecret = getenv('WEBHOOK_SECRET');

function verifyWebhookSignature($payload, $signature, $secret) {
    $expected = hash_hmac('sha256', json_encode($payload), $secret);
    return hash_equals($expected, $signature);
}

// Recibir webhook
$payload = json_decode(file_get_contents('php://input'), true);
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!verifyWebhookSignature($payload, $signature, $webhookSecret)) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

// Webhook verificado, procesar...
echo json_encode(['received' => true]);
?>
```

***

## Verificación en Go

```go theme={null}
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "net/http"
)

func verifyWebhookSignature(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    signature := r.Header.Get("X-Webhook-Signature")
    
    var payload map[string]interface{}
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    
    payloadBytes, _ := json.Marshal(payload)
    if !verifyWebhookSignature(payloadBytes, signature, webhookSecret) {
        http.Error(w, "Invalid signature", http.StatusUnauthorized)
        return
    }
    
    // Webhook verificado, procesar...
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"received": true}`))
}
```

***

## Regenerar Secret

Si crees que tu secret fue comprometido, puedes regenerarlo:

```bash theme={null}
curl -X POST https://api.whaapy.com/user-webhooks/<id>/regenerate-secret \
  -H "Authorization: Bearer wha_TU_API_KEY"
```

<Warning>
  Después de regenerar, actualiza tu código con el nuevo secret. Los webhooks con el secret anterior fallarán la verificación.
</Warning>

***

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Gestión de Webhooks" icon="gear" href="/api-reference/webhooks/management">
    Actualizar, probar y eliminar webhooks
  </Card>

  <Card title="Estructura de Payloads" icon="code" href="/api-reference/webhooks/payloads">
    Ver qué datos recibes en cada evento
  </Card>
</CardGroup>
