Blog
How to Secure Webhook Endpoints with HMAC
Integration Development
Integration Fundamentals
Dev Tips

How to Secure Webhook Endpoints with HMAC

Want to make sure only authorized systems can send data to webhook endpoints? Learn how to use HMAC to protect your data.
May 25, 2022
Last Updated: Jun 19, 2026
Bru Woodring
Bru WoodringTechnical Content Strategist
wixq4zxakgdibyqdvpnh

Key takeaways

  • Webhooks are powerful. Unsecured, they're a liability. Without verification, anyone can spoof a payload and send malicious data to your endpoint. Security isn't optional; it's foundational.
  • HMAC turns a shared secret into tamper-proof validation. HMAC uses the request body plus a secret key to generate a unique signature. The destination app recomputes the hash and compares it to the header. 
  • Hashing is a one-way street, and that's the point. Even if an attacker intercepts a request, they can't reverse-engineer the secret key from the hash. HMAC is one of the most reliable methods for webhook authentication.
  • HMAC is already the industry standard and is easy to implement. Slack, Dropbox, and Shopify all use HMAC with SHA-256 to secure webhook endpoints. With support across Node.js, Python, PHP, and .NET, a few lines of code can stand between your integration and a data integrity risk.

Introduction

Webhooks are ubiquitous in SaaS integrations, and there's a good reason for that. They are a simple and speedy way to transfer data via HTTP callbacks between systems based on changes to data in those systems. My company helps SaaS teams build native integrations to their customers' other apps, and many of our customers use webhooks for their integrations.

Over the last few weeks, we've helped several of our customers who needed to ensure that their webhook endpoints were secure. In this post, we'll describe the approach we recommend. But first, let's lay some groundwork.

How do webhooks work?

In short, the source app has a webhook, and the destination app has a webhook endpoint; based on some event occurring in the source app, the webhook sends an HTTP request to the webhook endpoint.

Here's a simple example of an HTTP request body (or payload):

123456789101112131415
{
"event": "WAREHOUSE_UPDATE",
"updates": [
{
"item": "gadgets",
"action": "add",
"quantity": 20
},
{
"item": "widgets",
"action": "remove",
"quantity": 10
}
]
}

But how do you ensure that the destination app receives valid data from the source app and not bogus data from a bad actor who has spoofed the webhook?

The short answer is that you need to set up the webhook to provide the endpoint with the HTTP request and a unique key that the endpoint can use to verify the data. But, before we get into the details, let's briefly cover hashing.

What is hashing?

At its simplest, hashing is the process of converting a value (or key) into another value. Even if you've not worked extensively with hashing before, you are probably aware of MD5, SHA-256, or RipeMD-128. Each of these is the name of a hashing algorithm (aka cryptographic hash function).

Let's see what each algorithm does to a classic string:

  • MD5 hashes Hello World! to ed076287532e86365e841e92bfc50d8c
  • SHA-256 hashes Hello World! to 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
  • RipeMD-128 hashes Hello World! to 24e23e5c25bc06c8aa43b696c1e11669

The important part is that an algorithm hashes a value the same way every time. If we don't change our string ('Hello World!'), the resulting hash value doesn't change either.

However, if anything in the string changes, the hash will also change. For example, let's lower-case the 'H' so we have 'hello World!' and see what that does:

  • MD5 hashes hello World! to 41d0c351efedf7fdb9a5dc8a1ed4b4e3
  • SHA-256 hashes hello World! to e4ad0102dc2523443333d808b91a989b71c2439d7362aca6538d49f76baaa5ca
  • RipeMD-128 hashes hello World! to b5cf338f17d6796ba0312e0d78c70831

A slight change, but the resulting differences are evident.

Though hashing doesn't allow us to completely solve our original problem (someone sending bogus data to a webhook endpoint), it does lead us directly to HMAC.

What is HMAC?

HMAC, or hashed message authentication code, is an authentication method that uses not one but two keys. The first key is the HTTP request body, while the second one is a secret cryptographic key. When you implement HMAC for your webhook, you'll be using both these keys plus an algorithm such as MD5, SHA-256, or RipeMD-128 to ensure the HTTP request that shows up at your webhook endpoint is legit.

How does HMAC work?

Before the source app sends an HTTP request via the webhook, it hashes the payload (request body) with HMAC using the secret key. The resulting hash is then bundled into the HTTP request as a header, and the entire request (header and body) is sent to the webhook endpoint.

Upon receiving the HTTP request, the destination app hashes the body with the secret key and then compares the result to the hash provided in the header. If the values match, the destination app knows the data is legit and processes it. If the values do not match, the destination app rejects the data and executes whatever code was written for that scenario – perhaps creating a log entry or sending a notification.

If someone tries to spoof the payload, they won't be able to generate a valid hash since they don't have the secret key. Door closed.

Let's imagine that you have an e-commerce platform connected to your app. Your app regularly sends payloads to the platform's webhook endpoint to create orders and issue refunds. Using HMAC ensures that you won't have random (or not so random) people sending bogus orders or refunds to the e-commerce platform.

But, you say, couldn't someone capture an HTTP request and reverse engineer the hash in the header to figure out the secret? Short answer: no. Hashing is a one-way function. To crack a hash with a sufficiently complex secret, we would need more computing power and time than any of us has available.

Apps that rely on HMAC for webhook endpoints

Some well-known apps currently use HMAC to secure their webhook endpoints:

  • Slack: Provides a Signing Secret when you create a Slack app. When it sends a webhook payload, it hashes both the payload and webhook's timestamp with that secret using SHA256. The webhook request includes the resulting hash as a header called X-Slack-Signature.
  • Dropbox: Generates an App Secret when you create a Dropbox app and uses that secret to generate webhook HMAC hashes and authenticate users with OAuth 2.0. It hashes webhook payloads using SHA256 and sends the hash as a header called X-Dropbox-Signature.
  • Shopify: Creates an API Secret Key and hashes its payloads with that key and SHA256. It sends the hash as a header called X-Shopify-Hmac-SHA256.

HMAC has broad language support

You can use just about any modern language to compute HMAC hashes. Here are some links to popular languages with HMAC capabilities:

If you're using Prismatic to build integrations, HMAC verification is built into the platform. Seee our HMAC documentation for how to configure it without writing custom verification code.

Example code for HMAC

Finally, what would all of this be without code? Here is an example of how this might be set up in NodeJS using the built-in crypto module:

12345678910111213141516
const crypto = require("crypto");
const SECRET_KEY = "secret-FA782CF7-060E-484E-B3DC-055CF2C9ED99";
const payload = JSON.stringify({
event: "REFUND_REQUEST",
user: "realcustomer@notabaddie.com",
amount: "50.25",
});
const hash = crypto
.createHmac("sha256", SECRET_KEY)
.update(payload, "utf-8")
.digest("hex");
console.log(hash); // Prints d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8

The corresponding HTTP request using HMAC might look like this:

1234
curl https://my.webhook.endpoint.com/callback \
--request POST \
--header "x-hmac-hash: d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8" \
--data '{"event":"REFUND_REQUEST","user":"realcustomer@notabaddie.com","amount":"50.25"}'

Even if a bad actor intercepted your HTTP request, they couldn't issue a refund request of a million dollars to their own email address, since they couldn't sign the request properly without the secret key.

Wondering how you are going to build all those integrations?

Check out our Build vs Buy guide to explore your options.

Read the guide

Conclusion

Using HMAC does not require that you learn a new language or gain an advanced understanding of encryption, but it does allow you to protect the integrity of the data you are transferring via webhooks.

Do you spend a lot of time building integrations to your customers' other apps? Connect with us to learn how an embedded iPaaS can expedite your integration delivery.

Common questions

Question: What is HMA, and how is it used for webhooks?

Answer: HMAC (hashed message authentication code) is an authentication method that generates a hash from two keys: an HTTP request body and a secret cryptographic key. When a source app sends a webhook, it hashes the payload using HMAC and a shared secret, then includes the resulting hash as an HTTP header. When the destination app receives the request, it independently generates its own hash from the same payload and secret. If the two hashes match, the payload is verified and processed; if they don't, it's rejected. 

Question: Why is HMAC preferred over API keys for securing webhook endpoints?

Answer: Prismatic recommends HMAC because it verifies both the origin and integrity of a webhook payload. In contrast, a static API key only confirms that the sender knew the key, not that the payload was unaltered in transit. With HMAC, even if an attacker intercepts a valid request, they cannot forge a new one or tamper with the payload without knowing the secret key. The hash changes if even a single character of the payload changes, making HMAC a more robust defense for event-driven integrations. It also doesn't require learning advanced cryptography or a new language.

Question: What hashing algorithm should I use with HMAC for webhooks?

Answer: HMAC supports several hashing algorithms, including MD5, SHA-256, and RipeMD-128. For webhook security, SHA-256 is the recommended choice and the industry standard. It strikes the right balance between security and performance, and is the algorithm used by major SaaS platforms like Slack, Shopify, and Dropbox for their webhook implementations. Avoid MD5, as it has known vulnerabilities. Prismatic's built-in HMAC webhook trigger also uses SHA-256 by default, making it straightforward to align with common third-party conventions.

Question: What is a replay attack, and does HMAC protect against it?

Answer: A replay attack occurs when a bad actor captures a legitimate, already-signed webhook request and resends it to your endpoint, triggering an unintended action – such as reprocessing a payment or order. Basic HMAC alone does not protect against replay attacks, since a captured request contains a valid hash. To add replay protection, some platforms (like Slack) include a timestamp in the HMAC signature computation. Your endpoint can then reject any request whose timestamp falls outside an acceptable window. If you need this behavior, Prismatic supports building a custom webhook trigger that incorporates timestamp validation alongside HMAC verification.

Question: How do I implement HMAC webhook verification in Node.js?

Answer: Node.js includes a built-in crypto module that makes HMAC verification straightforward. Here's a basic example:

123456789
const crypto = require("crypto");
const SECRET_KEY = "your-secret-key";
const payload = JSON.stringify({ event: "ORDER_CREATED", amount: "99.99" });
const hash = crypto
.createHmac("sha256", SECRET_KEY)
.update(payload, "utf-8")
.digest("hex");

The resulting hash is sent as an HTTP header (e.g., x-hmac-hash). On the receiving end, you perform the same computation and compare your hash to the one in the incoming header. If they match, the payload is authentic. No third-party libraries are required — the crypto module handles everything natively.

Question: Why should I use timingSafeEqual instead of === when comparing HMAC hashes?

Answer: Using === to compare two hash strings is vulnerable to timing attacks. Because JavaScript's string comparison exits as soon as it finds a mismatched character, an attacker can measure tiny differences in response time to statistically infer correct hash values character by character. The Node.js crypto.timingSafeEqual() function eliminates this risk by always taking the same amount of time to compare two buffers, regardless of where they differ. Always use it when verifying HMAC signatures:

1234
const isValid = crypto.timingSafeEqual(
Buffer.from(receivedHash),
Buffer.from(expectedHash)
);

This is a small but critical security detail that protects your endpoint from side-channel exploitation.

Question: Which popular SaaS apps use HMAC to secure their webhook endpoints?

Answer: Many widely used SaaS platforms rely on HMAC with SHA-256 to authenticate webhook requests:

  • Slack – Provides a Signing Secret when you create a Slack app. It hashes both the request body and a timestamp using SHA-256, sending the result as the X-Slack-Signature header.
  • Shopify – Generates an API Secret Key and hashes payloads with SHA-256, sending the result as X-Shopify-Hmac-SHA256.
  • Dropbox – Creates an App Secret used to generate HMAC hashes and authenticate OAuth 2.0 flows, sending the hash as X-Dropbox-Signature.

Because each platform may implement HMAC slightly differently (for example, Slack's timestamp inclusion), Prismatic provides both a built-in trigger for standard cases and a custom trigger path for non-standard implementations.

Question: Does Prismatic handle HMAC webhook verification automatically?

Answer: Yes, for most standard implementations. Prismatic's built-in HMAC webhook trigger (part of the Hash component) handles verification automatically when the webhook request body is hashed with a string secret key and the hash is passed as a header. Many built-in component triggers already include this validation out of the box. For non-standard implementations (such as Slack's timestamp-inclusive signature scheme), you can build a custom webhook trigger that implements your own HMAC logic. 

Get a Demo

Ready to ship integrations 10x faster?

Join teams from Fortune 500s to high-growth startups that have transformed integrations from a bottleneck into a growth driver.