PlexySDK DOCS

Secure Webhooks

Verify webhook signatures to ensure authenticity

Secure Webhooks

Verify webhook signatures to ensure requests genuinely come from Plexy.

Why verify signatures?

Without verification, attackers could send fake webhook events to your endpoint. Signature verification ensures:

  • The request came from Plexy
  • The payload hasn't been tampered with
  • The request is recent (not a replay attack)

Webhook signing secret

Each webhook endpoint has a unique signing secret. Find it in your Dashboard:

  1. Go to Developers > Webhooks
  2. Click your endpoint
  3. Copy the Signing Secret (starts with whsec_)

Keep your signing secret secure. Never expose it in client-side code or version control.

Signature header

Plexy includes a signature in the Plexy-Signature header:

Plexy-Signature: t=1679529600,v1=abc123...
PartDescription
tUnix timestamp when signature was generated
v1HMAC-SHA256 signature

Verify signatures

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const parts = signature.split(',');
const timestamp = parts[0].split('=')[1];
const receivedSig = parts[1].split('=')[1];

// Check timestamp is recent (within 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - parseInt(timestamp) > 300) {
throw new Error('Webhook timestamp too old');
}

// Compute expected signature
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
const expectedSig = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');

// Compare signatures
if (!crypto.timingSafeEqual(
Buffer.from(receivedSig),
Buffer.from(expectedSig)
)) {
throw new Error('Invalid webhook signature');
}

return true;
}

app.post('/webhooks/plexy', express.json(), (req, res) => {
const signature = req.headers['plexy-signature'];

try {
verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(err.message);
}

// Process verified event
handleEvent(req.body);
res.status(200).send('OK');
});
import hmac
import hashlib
import time

def verify_webhook_signature(payload, signature, secret):
    parts = dict(p.split('=') for p in signature.split(','))
    timestamp = parts['t']
    received_sig = parts['v1']

    # Check timestamp is recent (within 5 minutes)
    current_time = int(time.time())
    if current_time - int(timestamp) > 300:
        raise ValueError('Webhook timestamp too old')

    # Compute expected signature
    signed_payload = f"{timestamp}.{payload}"
    expected_sig = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Compare signatures
    if not hmac.compare_digest(received_sig, expected_sig):
        raise ValueError('Invalid webhook signature')

    return True

@app.route('/webhooks/plexy', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('Plexy-Signature')

    try:
        verify_webhook_signature(
            request.data.decode(),
            signature,
            os.environ['WEBHOOK_SECRET']
        )
    except ValueError as e:
        return str(e), 400

    # Process verified event
    handle_event(request.json)
    return 'OK', 200
func verifyWebhookSignature(payload []byte, signature, secret string) error {
    parts := strings.Split(signature, ",")
    timestamp := strings.Split(parts[0], "=")[1]
    receivedSig := strings.Split(parts[1], "=")[1]

    // Check timestamp is recent
    ts, _ := strconv.ParseInt(timestamp, 10, 64)
    if time.Now().Unix()-ts > 300 {
        return errors.New("webhook timestamp too old")
    }

    // Compute expected signature
    signedPayload := fmt.Sprintf("%s.%s", timestamp, string(payload))
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signedPayload))
    expectedSig := hex.EncodeToString(mac.Sum(nil))

    // Compare signatures
    if !hmac.Equal([]byte(receivedSig), []byte(expectedSig)) {
        return errors.New("invalid webhook signature")
    }

    return nil

}

Using SDKs

Plexy SDKs provide built-in signature verification:

const plexy = require('@plexy/plexy-web');

app.post('/webhooks/plexy', express.raw({type: 'application/json'}), (req, res) => {
  const event = plexy.webhooks.constructEvent(
    req.body,
    req.headers['plexy-signature'],
    process.env.WEBHOOK_SECRET
  );

  // Event is verified
  handleEvent(event);
  res.status(200).send('OK');
});

See also

На этой странице