Webhook Security

Properly securing your webhook endpoints is critical to prevent unauthorized access and data tampering.

Secret Verification

Every webhook request includes an X-Webhook-Secret header containing your unique webhook secret. Always verify this header before processing the request.

How It Works

  1. When you create a webhook, PDF-Sign generates a unique secret (format: whsec_...)
  2. This secret is included in the X-Webhook-Secret header of every request
  3. Your endpoint should verify the secret matches before processing

Example Verification

// Node.js / Express
app.post('/webhooks/pdf-sign', (req, res) => {
  const receivedSecret = req.headers['x-webhook-secret'];
  const expectedSecret = process.env.PDF_SIGN_WEBHOOK_SECRET;

  if (receivedSecret !== expectedSecret) {
    console.error('Invalid webhook secret');
    return res.status(401).json({ error: 'Unauthorized' });
  }

  // Process the webhook...
  res.status(200).json({ received: true });
});
# Python / Flask
from flask import Flask, request, jsonify
import os

app = Flask(__name__)

@app.route('/webhooks/pdf-sign', methods=['POST'])
def handle_webhook():
    received_secret = request.headers.get('X-Webhook-Secret')
    expected_secret = os.environ.get('PDF_SIGN_WEBHOOK_SECRET')

    if received_secret != expected_secret:
        return jsonify({'error': 'Unauthorized'}), 401

    # Process the webhook...
    return jsonify({'received': True}), 200

Never Skip Verification

Without secret verification, anyone who discovers your endpoint URL could send fake webhook requests.

Secure Your Secret

Do's

  • Store in environment variables - Never hardcode secrets
  • Use secret management - Consider using AWS Secrets Manager, HashiCorp Vault, etc.
  • Rotate periodically - Create new webhooks and delete old ones regularly
  • Limit access - Only give secret access to services that need it

Don'ts

  • Never commit to source control - Add .env to .gitignore
  • Never log the secret - Avoid printing secrets in logs
  • Never expose in client-side code - Secrets are for server-side only
  • Never share via insecure channels - Don't send secrets in plain email/chat

HTTPS Requirement

PDF-Sign only sends webhooks to HTTPS endpoints. This ensures:

  • Encryption in transit - Data cannot be intercepted
  • Server authentication - You know you're sending to the right server
  • Data integrity - Payloads cannot be tampered with

Development Testing

For local development, use tools like ngrok or localtunnel to create an HTTPS tunnel to your local server.

Additional Headers

Use additional headers for extra verification:

HeaderPurpose
X-Webhook-EventVerify the event type matches expected
X-Webhook-TimestampCheck request freshness (reject old requests)
User-AgentVerify it's PDF-Sign-Webhook/1.0

Timestamp Verification

Prevent replay attacks by rejecting old requests:

const timestamp = parseInt(req.headers['x-webhook-timestamp']);
const now = Date.now();
const fiveMinutes = 5 * 60 * 1000;

if (Math.abs(now - timestamp) > fiveMinutes) {
  return res.status(400).json({ error: 'Request too old' });
}

IP Allowlisting

For additional security, you can restrict your endpoint to only accept requests from PDF-Sign's servers. Contact support for the current IP ranges.

Dynamic IPs

Our IP ranges may change. Subscribe to our status page for notifications about infrastructure changes.

Error Handling

Handle errors gracefully without exposing internal details:

app.post('/webhooks/pdf-sign', (req, res) => {
  try {
    // Verify and process...
  } catch (error) {
    // Log internally
    console.error('Webhook processing error:', error);

    // Return generic error (don't expose details)
    res.status(500).json({ error: 'Internal error' });
  }
});

Rate Limiting

Protect your endpoint from abuse with rate limiting:

const rateLimit = require('express-rate-limit');

const webhookLimiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minute
  max: 100, // Max 100 requests per minute
  message: { error: 'Too many requests' }
});

app.post('/webhooks/pdf-sign', webhookLimiter, (req, res) => {
  // Handle webhook...
});

Security Checklist

Before going to production, ensure:

  • Secret verification is implemented
  • Secret is stored in environment variables
  • Endpoint uses HTTPS
  • Timestamp verification (optional but recommended)
  • Error handling doesn't expose internals
  • Rate limiting is configured
  • Logging doesn't include sensitive data
  • Access to webhook secret is restricted

Reporting Security Issues

If you discover a security vulnerability in PDF-Sign's webhook system, please report it responsibly to security@pdf-sign.com. We take all reports seriously and will respond promptly.

Next Steps