Webhook İmza Doğrulama

Webhook isteklerinin güvenliğini sağlamak için imza doğrulama nasıl yapılır

Genel Bakış

Wespoke, gönderdiği tüm webhook isteklerini bir HMAC-SHA256 imzası ile imzalar. Bu imzayı doğrulayarak webhook'un gerçekten Wespoke'tan geldiğinden emin olabilirsiniz.

Önemli: Webhook imza doğrulamasını uygulamanız şiddetle önerilir. Bu, kötü niyetli kullanıcıların sahte webhook istekleri göndermesini engeller.

İmza Header'ı

Her webhook isteği X-Wespoke-Signature header'ı içerir. Bu header, isteğin gövdesinin HMAC-SHA256 hash'ini içerir.

Örnek Header
POST /webhook HTTP/1.1
Host: your-server.com
Content-Type: application/json
X-Wespoke-Signature: sha256=1234567890abcdef...
X-Wespoke-Timestamp: 1696774496789

{
  "event": "call.started",
  "data": {...}
}

İmza Hesaplama

İmza, webhook secret'ınız kullanılarak şu şekilde hesaplanır:

HMAC-SHA256(secret, timestamp + "." + request_body)

Webhook secret'ınızı kontrol panelinde Ayarlar → Webhooks bölümünden alabilirsiniz.

Node.js Örneği

Express.js ile İmza Doğrulama
const crypto = require('crypto');
const express = require('express');
const app = express();

// Raw body'yi almak için
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString();
  }
}));

function verifyWebhookSignature(req, secret) {
  const signature = req.headers['x-wespoke-signature'];
  const timestamp = req.headers['x-wespoke-timestamp'];

  if (!signature || !timestamp) {
    return false;
  }

  // Zaman damgası kontrolü (5 dakikadan eski istekleri reddet)
  const currentTime = Date.now();
  if (Math.abs(currentTime - parseInt(timestamp)) > 5 * 60 * 1000) {
    return false;
  }

  // İmza hesaplama
  const payload = `${timestamp}.${req.rawBody}`;
  const expectedSignature = 'sha256=' +
    crypto
      .createHmac('sha256', secret)
      .update(payload)
      .digest('hex');

  // Timing attack'lara karşı güvenli karşılaştırma
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

app.post('/webhook', (req, res) => {
  const webhookSecret = process.env.WESPOKE_WEBHOOK_SECRET;

  if (!verifyWebhookSignature(req, webhookSecret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // İmza doğrulandı, webhook işle
  const { event, data } = req.body;

  // Olayı işle...
  console.log(`Webhook received: ${event}`);

  res.status(200).json({ received: true });
});

app.listen(3000);

Python Örneği

Flask ile İmza Doğrulama
import hmac
import hashlib
import time
from flask import Flask, request, jsonify

app = Flask(__name__)

def verify_webhook_signature(request, secret):
    signature = request.headers.get('X-Wespoke-Signature')
    timestamp = request.headers.get('X-Wespoke-Timestamp')

    if not signature or not timestamp:
        return False

    # Zaman damgası kontrolü (5 dakikadan eski istekleri reddet)
    current_time = int(time.time() * 1000)
    if abs(current_time - int(timestamp)) > 5 * 60 * 1000:
        return False

    # İmza hesaplama
    payload = f"{timestamp}.{request.get_data(as_text=True)}"
    expected_signature = 'sha256=' + hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Timing attack'lara karşı güvenli karşılaştırma
    return hmac.compare_digest(signature, expected_signature)

@app.route('/webhook', methods=['POST'])
def webhook():
    webhook_secret = os.environ.get('WESPOKE_WEBHOOK_SECRET')

    if not verify_webhook_signature(request, webhook_secret):
        return jsonify({'error': 'Invalid signature'}), 401

    # İmza doğrulandı, webhook işle
    data = request.json
    event = data.get('event')

    # Olayı işle...
    print(f"Webhook received: {event}")

    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

PHP Örneği

PHP ile İmza Doğrulama
<?php

function verifyWebhookSignature($secret) {
    $signature = $_SERVER['HTTP_X_WESPOKE_SIGNATURE'] ?? '';
    $timestamp = $_SERVER['HTTP_X_WESPOKE_TIMESTAMP'] ?? '';

    if (empty($signature) || empty($timestamp)) {
        return false;
    }

    // Zaman damgası kontrolü (5 dakikadan eski istekleri reddet)
    $currentTime = round(microtime(true) * 1000);
    if (abs($currentTime - intval($timestamp)) > 5 * 60 * 1000) {
        return false;
    }

    // İmza hesaplama
    $rawBody = file_get_contents('php://input');
    $payload = $timestamp . '.' . $rawBody;
    $expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);

    // Timing attack'lara karşı güvenli karşılaştırma
    return hash_equals($signature, $expectedSignature);
}

// Webhook endpoint
$webhookSecret = getenv('WESPOKE_WEBHOOK_SECRET');

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

// İmza doğrulandı, webhook işle
$data = json_decode(file_get_contents('php://input'), true);
$event = $data['event'] ?? '';

// Olayı işle...
error_log("Webhook received: " . $event);

http_response_code(200);
echo json_encode(['received' => true]);

Güvenlik En İyi Uygulamaları

  • Her zaman webhook imzasını doğrulayın
  • Zaman damgası kontrolü yaparak replay attack'ları önleyin
  • Webhook secret'ınızı güvenli bir şekilde saklayın (ortam değişkenleri kullanın)
  • HTTPS kullanarak webhook endpoint'inizi güvenli hale getirin
  • Timing-safe karşılaştırma fonksiyonları kullanın
  • Webhook secret'ınızı periyodik olarak yenileyin

Test Etme

Webhook imza doğrulamanızı test etmek için kontrol panelinde bulunan webhook test aracını kullanabilirsiniz. Bu araç, gerçek bir webhook isteği simüle eder ve imza doğrulamanızın doğru çalışıp çalışmadığını kontrol etmenizi sağlar.

Webhook Test Aracına Git