Skip to content

Sicurezza Webhook

Tutte le consegne webhook sono firmate usando HMAC-SHA256 così puoi verificare che le richieste provengano realmente da Replicer.

Meccanismo di firma

Ogni endpoint webhook ha un signing secret univoco (mostrato una sola volta alla creazione). Replicer firma ogni payload con questo secret e include la firma nell'header X-Replicer-Signature.

La firma viene calcolata come:

HMAC-SHA256(signing_secret, request_body)

Verificare le firme

Node.js

javascript
import crypto from 'crypto'

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

// Middleware Express
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-replicer-signature']
  const secret = process.env.WEBHOOK_SECRET

  if (!verifyWebhookSignature(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Firma non valida' })
  }

  const event = JSON.parse(req.body)
  // Processa l'evento...
  
  res.status(200).json({ received: true })
})

Python

python
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# Esempio Flask
@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Replicer-Signature')
    secret = os.environ['WEBHOOK_SECRET']
    
    if not verify_webhook(request.data, signature, secret):
        return jsonify({'error': 'Firma non valida'}), 401
    
    event = request.get_json()
    # Processa l'evento...
    
    return jsonify({'received': True}), 200

Go

go
func verifySignature(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(signature), []byte(expected))
}

Header aggiuntivi

Usa questi header per una verifica extra:

HeaderDescrizione
X-Replicer-TimestampTimestamp Unix di quando l'evento è stato inviato
X-Replicer-Delivery-IdID univoco per questo tentativo di consegna
X-Replicer-EventTipo di evento (es. call.ended)

Best practice

Raccomandazioni di sicurezza

  • Verifica sempre le firme prima di processare gli eventi webhook
  • Usa timingSafeEqual (o equivalente) per prevenire timing attack
  • Conserva i secret in modo sicuro in variabili d'ambiente o secret manager
  • Rispondi rapidamente — processa in modo asincrono e restituisci 200 immediatamente
  • Sii idempotente — potresti ricevere lo stesso evento più volte a causa dei retry
  • Controlla il timestamp — rifiuta eventi più vecchi di 5 minuti per prevenire replay attack

Verifica del timestamp

Per prevenire replay attack, verifica l'header X-Replicer-Timestamp:

javascript
const timestamp = parseInt(req.headers['x-replicer-timestamp'])
const now = Math.floor(Date.now() / 1000)
const fiveMinutes = 5 * 60

if (Math.abs(now - timestamp) > fiveMinutes) {
  return res.status(401).json({ error: 'Timestamp troppo vecchio' })
}

Replicer API Documentation