Webhook Signing Methods Compared: HMAC vs Public-Key
Webhook Signing Methods Compared (HMAC vs Public-Key Signatures)
Webhook signing ensures the authenticity of incoming requests. This guide helps you compare webhook signing methods hmac vs rsa vs ecdsa across popular providers like Stripe, Paddle, SendGrid, and Mailgun. You’ll also learn how to design a unified verification layer in Laravel.
Two families of webhook signatures
Shared secret HMAC (fast, simple)
HMAC-based signatures use a shared secret to generate a hash of the payload. This method is:
- Fast and computationally inexpensive.
- Simple to implement but requires secure secret management.
This family is the usual choice when you need to compare webhook signing methods hmac vs rsa vs ecdsa for performance and simplicity trade-offs.
Public-key signatures (rotation-friendly, more moving parts)
Public-key signatures use asymmetric cryptography, where the provider signs the payload with a private key, and you verify it with a public key. This method is:
- Rotation-friendly (keys can be updated without sharing secrets).
- More complex, requiring key management and additional processing.
Suggested diagram: A comparison table showing HMAC vs public-key methods (e.g., speed, complexity, rotation).
What major providers do (high-level)
Stripe: verify using payload + Stripe-Signature + endpoint secret
Stripe signs the raw payload and includes the signature in the Stripe-Signature header. Use their library or manually verify by:
- Extracting the timestamp and signature from the header.
- Generating an HMAC-SHA256 hash of the payload.
- Comparing the computed hash with the signature.
Paddle: secret-based signature verification
Paddle uses a shared secret to sign the payload. Verify by:
- Concatenating the payload fields.
- Generating an HMAC-SHA256 hash.
- Comparing the computed hash with the signature.
Learn more in Paddle Developer Docs.
SendGrid: signed event webhook + public key management
SendGrid signs webhook events using a private key. Verify by:
- Fetching the public key from their endpoint.
- Verifying the signature against the payload.
Mailgun: timestamp+token → HMAC-SHA256 → compare to signature
Mailgun includes a timestamp, token, and signature in webhook requests. Verify by:
- Concatenating the timestamp and token.
- Generating an HMAC-SHA256 hash.
- Comparing the computed hash with the signature.
Mini incident: A team once ignored Mailgun’s timestamp validation, leading to replay attacks. Always validate timestamps.
Designing your verification layer (adapter pattern)
Micro checklist:
- Detect provider by signature headers
- Always verify using the
rawbody- Normalize events to
verified/rejected/replay suspected- Centralize logging for mismatches
Identify provider by headers
Use headers to determine the provider and route the request to the appropriate verification logic. For example:
| |
Always verify using raw body
Always use the raw body for signature generation. Parsed payloads can introduce whitespace or order changes, invalidating the signature.
Emit consistent “verified / rejected / replay suspected”
Standardize your verification responses to:
verified: Signature matches.rejected: Signature mismatch.replay suspected: Timestamp outside the allowed window.
Start with Sample Project and confirm verification in Message Log.
Operational choices
Secret rotation
Rotate secrets regularly to minimize exposure. Use environment variables to manage secrets securely.
Timestamp windows + replay protection
Validate timestamps within a ±5-minute window to prevent replay attacks. Reject requests outside this window.
Logging without leaking keys
Log verification failures without exposing secrets or signatures. For example:
| |
Micro checklist:
- Rotate secrets every 90 days.
- Validate timestamps within ±5 minutes.
- Use environment variables for secrets.
- Log failures securely.
- Test with local curl vectors.
Common failure modes
- Not using raw body (breaks Stripe/Paddle-style verification frequently). (Stripe Docs)
- Wrong secret/public key (environment mismatch). (Stripe Docs)
- Signature parsed incorrectly (multiple signatures, comma-separated parts).
- Timestamp window ignored → replay attacks possible (Mailgun explicitly recommends replay protection). (Mailgun Docs)
- Logging secrets/signatures → incident risk.
- Treating verification as “optional in dev” → production drift.
Learn more about common mismatch causes.
Related docs:
- /docs/webhooks/ — SendPromptly webhook signature headers + retries
- /docs/guides/webhook-signature-verification-cookbook/ — Copy-paste Laravel verification
- /docs/guides/webhook-security-checklist/ — Harden the full pipeline
Conclusion
If you integrate multiple providers, standardize on one ‘verified’ event and audit it in Message Log.
Key takeaways
- Compare HMAC and public-key methods based on your needs.
- Always verify using raw body and validate timestamps.
- Rotate secrets regularly and log failures securely.
- Use an adapter pattern to handle multiple providers.
- Test with local curl vectors to debug issues.