Skip to main content
US Non-VoIP Numbers99% SMS Code Reception Success RateVerified in 5โ€“30 seconds
RCVSMS
โ† Back to blog
APIยท 10 min read

SMS Verification Webhook Security: 7 Best Practices (2026)

How to secure your SMS verification webhooks against replay attacks, man-in-the-middle, and unauthorized access. With production code examples.

Omar Hassan

Why webhook security matters

Webhooks are the most common attack surface for SMS APIs. A compromised webhook lets attackers:

  • Trigger SMS rentals on your account (costing money)
  • Intercept verification codes (breaking security)
  • Cause downtime by spamming your endpoint

7 security practices

1. Verify HMAC signatures

Every webhook should include a cryptographic signature. RCVSMS uses HMAC-SHA256.

2. Use HTTPS only

Never accept webhooks over HTTP. Most webhook providers (Stripe, Twilio, etc.) reject HTTP.

3. Implement timestamp validation

Reject events older than 5 minutes to prevent replay attacks.

4. Use unique, random webhook secrets

Don't reuse passwords or other API keys.

5. Whitelist source IPs

RCVSMS publishes its webhook IP ranges.

6. Idempotency keys

Use the event ID to prevent double-processing.

7. Rate limit your endpoint

Even with signature verification, protect against DoS.

Code example (Node.js)

const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/webhooks/sms', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-rcvsms-signature'];
  const timestamp = req.headers['x-rcvsms-timestamp'];
  const body = req.body;
  
  // Verify timestamp (5 minute window)
  const ageSeconds = (Date.now() / 1000) - parseInt(timestamp);
  if (ageSeconds > 300) {
    return res.status(401).send('Stale timestamp');
  }
  
  // Verify HMAC
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${timestamp}.${body.toString()}`)
    .digest('hex');
  
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = JSON.parse(body);
  handleEvent(event);
  res.status(200).send('OK');
});

Testing

Test your webhook handler with the RCVSMS CLI:

rcvsms webhook:test --url https://your-app.com/webhooks/sms

This sends a signed test event you can use to validate your implementation.

Ready to rent your first number?

Get started in under 2 minutes. No subscription required.

Get started for $0.24