DEV Community

Sospeter Mong'are
Sospeter Mong'are

Posted on

How to Protect/secure Your Callback & Webhook Endpoints

When integrating with APIs from banks, payment providers, telecoms, or SaaS services, you’ll often need to expose a callback URL or webhook endpoint. This is a publicly accessible URL where an external service sends real-time notifications about events (e.g., payment confirmations, message delivery receipts, or system alerts).

Because these URLs are public-facing, they can become a target for attacks. Here’s how to design secure, reliable endpoints, regardless of your programming language or framework.


Callback vs Webhook

Feature Callback Webhook
Usage A one-time response to a request (e.g., transaction status) A persistent URL for continuous event delivery (e.g., GitHub push events)
Trigger Linked to a single API call Triggered by system events automatically
Examples Bank transfer status, delivery confirmation Payment notifications, CI/CD triggers

Both need strong security because they expose your backend to external traffic.


1️. Use HTTPS Everywhere

Always secure your endpoint with HTTPS to protect data in transit. Without encryption, attackers can sniff or tamper with sensitive payloads.

  • Use certificates from Let’s Encrypt, Cloudflare, AWS ACM, or similar.
  • Reject plain HTTP requests.

2. Restrict Access by IP

If your provider uses fixed IP ranges, restrict requests to only those IPs:

Pseudocode:

allowed_ips = ["203.0.113.45", "198.51.100.23"]

function handleCallback(request):
    if request.ip not in allowed_ips:
        return 403 Forbidden
    processRequest(request)
Enter fullscreen mode Exit fullscreen mode

✅ Great for trusted integrations like banks.
⚠️ Not effective if the provider uses dynamic or cloud-based IP ranges.


3️. Authenticate Requests with Shared Secrets

A common pattern: exchange a shared secret token with your provider. They include this token in each request header, and your system verifies it.

Pseudocode:

shared_secret = "MY_SECRET"

function handleCallback(request):
    if request.headers["X-Secret"] != shared_secret:
        return 403 Forbidden
    processRequest(request)
Enter fullscreen mode Exit fullscreen mode

4. Verify Payload Integrity with Signatures (HMAC)

To ensure data hasn’t been tampered with, many services provide a signature header. You can recompute the hash on your end and compare.

Pseudocode:

secret_key = "MY_SECRET"
payload = request.body
signature_from_provider = request.headers["X-Signature"]

computed_signature = HMAC_SHA256(secret_key, payload)

if computed_signature != signature_from_provider:
    return 403 Forbidden
processRequest(request)
Enter fullscreen mode Exit fullscreen mode

✅ Strong security: protects against replay and tampering.


5. Log All Incoming Requests

Always record requests before processing. This helps with debugging, auditing, and compliance.

Pseudocode:

function logRequest(payload):
    writeToFile("logs.txt", timestamp() + " " + payload)
Enter fullscreen mode Exit fullscreen mode

6. Use Rate Limiting & Firewalls

Prevent abuse or denial-of-service (DoS) attacks by applying limits:

  • Rate limiting: Allow a max number of requests per second.
  • WAF/Firewall: Block suspicious IPs or countries.

Example tools: Nginx rate limiting, Cloudflare WAF, AWS WAF, or API Gateway throttling.


7. Respond Quickly, Process Asynchronously

Callbacks and webhooks often expect a fast 200 OK response. Don’t perform heavy processing immediately:

  1. Validate & acknowledge receipt quickly.
  2. Queue processing in a background worker (Celery, RabbitMQ, AWS SQS, etc.).

Security Best Practice Checklist

Practice Why It Matters
✅ HTTPS Encrypts communication and prevents sniffing
✅ IP Whitelisting Blocks unauthorized sources
✅ Shared Secrets Ensures only trusted systems can send data
✅ HMAC or Signatures Validates payload integrity and authenticity
✅ Logging Aids debugging, security reviews, and auditing
✅ Rate Limiting Mitigates brute-force or flood attacks
✅ Async Processing Improves reliability and avoids timeouts

Takeaway

Your callback or webhook endpoint is effectively a public API into your system. Treat it like any other production API:

  • Authenticate every request (token, signature, or both).
  • Restrict traffic (IP filters, firewalls).
  • Log everything (audit trail).
  • Respond fast and move processing offline.

Whether you’re working with Python, PHP, Node.js, Java, or Go, these principles apply universally. The implementation details may vary, but the security fundamentals remain the same.

Top comments (0)