Webhooks are one of the most common integration patterns today. They’re also one of the easiest places to make security mistakes.
In this post, I’ll focus on why HMAC is a better approach for securing webhooks and how Spubhi keeps the implementation simple and predictable.
This is not a comparison-heavy or theory-heavy article — it’s about practical implementation and reliability.
The Core Problem with Webhook Security
A webhook endpoint is publicly accessible by design.
That means anyone can send a request to it unless you validate the sender.
The real challenge is not adding authentication, but ensuring that:
The request was not modified
The sender actually knows a shared secret
The validation works reliably across different clients and platforms
This is where many webhook implementations break down.
Why HMAC Works Well for Webhooks
HMAC (Hash-based Message Authentication Code) solves a very specific problem:
verifying both authenticity and integrity of a request.
When using HMAC:
The sender signs the raw request body using a shared secret
The receiver independently generates the same signature
If the signatures match, the request is trusted
No secrets are sent over the wire.
Only a signature is transmitted.
This has a few important implications:
1. No secret exposure
Unlike tokens or passwords, the shared secret is never transmitted in the request.
2. Payload integrity is guaranteed
If even one character in the request body changes, the signature validation fails.
3. Stateless validation
The server does not need to store sessions or tokens per request.
It only needs the shared secret and the incoming payload.
4. Language-agnostic
HMAC works the same way in Java, Node.js, Python, Go, or any other language.
This makes it ideal for webhook-based systems.
Common HMAC Implementation Pitfalls
Most HMAC failures don’t come from cryptography — they come from inconsistencies.
Some common issues:
Signing parsed JSON instead of the raw body
Different JSON serialization on sender and receiver
Header mismatches or late header injection
Async signing issues in tools like Postman
A reliable HMAC implementation must:
Use the exact raw request body
Normalize the payload consistently
Use a predictable header for the signature
Validate before any business logic executes
Why Spubhi Keeps HMAC Simple
Spubhi was designed with webhook security as a first-class concern.
Here’s what Spubhi does differently:
1. Raw body handling is explicit
Spubhi validates HMAC against the actual incoming payload, not a re-serialized version.
This avoids subtle mismatches that break signature verification.
2. Header-based authentication only
Spubhi expects the HMAC signature in a single, clear header:
X-SPUBHI-SECRET
No ambiguity. No hidden behavior.
3. No encoding or decoding of secrets
Secrets are treated as-is.
There’s no unnecessary base64 wrapping or transformation.
What you sign on the client is exactly what Spubhi verifies on the server.
4. Authentication happens before flow execution
If authentication fails, the request is rejected immediately.
No partial execution, no side effects.
This makes webhook behavior predictable and safe.
Minimal Client-Side HMAC Example
// Normalize JSON exactly like your current logic
const body = JSON.stringify(JSON.parse(pm.request.body.raw));
// HMAC secret
const secret = "hmac_Qv33h2Yp7SpLwl7mQ8geU7R8SrfRZ27BpX5j0tHM";
// Encode inputs
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
const bodyData = encoder.encode(body);
// Generate HMAC-SHA-256
crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: "SHA-512" },
false,
["sign"]
).then(key => {
return crypto.subtle.sign("HMAC", key, bodyData);
}).then(signature => {
const hmac = Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
// SAME header name you already use
pm.request.headers.upsert({
key: "X-SPUBHI-SECRET",
value: hmac
});
});
That’s it.
No SDKs.
No custom formats.
No hidden transformations.
**
Minimal Server-Side Validation Logic**
On the server:
- Read the raw request body
- Generate HMAC using the same secret
- Compare it with the incoming header
- Reject if it doesn’t match
Simple logic — easy to audit, easy to debug.
Final Thoughts
Webhook security doesn’t need to be complex — it needs to be correct and consistent.
HMAC works well because it:
Protects payload integrity
Avoids secret exposure
Scales without state
Works across platforms
Spubhi keeps this model simple by:
Avoiding magic
Enforcing clear boundaries
Treating authentication as a first step, not an afterthought
If you’re building webhook-driven integrations, HMAC done right is enough — and simplicity is what makes it reliable.
Top comments (0)