I have two kind of related questions, one is specific two webhooks security in general and the other about how Zapier triggers webhooks.
1. I’ve read a lot about webhooks recently. Every piece of information I read points out that, for security reasons, it’s best to use shared secrets and sign the payload (usually with HMAC) when sending a webhook request. I don’t quite understand this hassle.
Imagine I have a server side app. I want my app to receive a webhook from a different app (in other words, I want to subsribe to an event in a different app). So I create an URL in my app to accept the webhook. My app also has its own HTTP API that anyone authorized can call. Now the question. Why the webhook URL should have a different security appoach than any other API my app has? If the general API endpoints has authentication mechanism (let’s say with simple API key), why can’t the webhook URL use the same approach?
Well, I understand it can actually. What I don’t understand is that why, when it comes to webhooks, almost everyone says about using signature and preventing reply attacks and almost no one says the same when it comes to regular API calls? What is the crucial difference here? Let’s say we talk about HTTPS in both cases (obviously).
Just to clarify what I’m talking about, here is the doc from Zapier with best practices how to implement webhooks security: https://resthooks.org/docs/security/
2. Zaper has Webhooks app that can trigger outgoing webhooks. In the light of the above said, why it doesn’t seem to support signing the webhook payload? It’s especially strange, if we consider the suggestions in the doc from Zapier menioned above.
Could someone explain to me what I’m missing here?
Best answer by ZaneView original
Great question. Let’s first clarify the context. Are you asking from the point of view of a user using the Webhooks by Zapier built-in app, or are you building a custom Zapier integration for your API/product?
TLDR for app developers - If your API signs its webhook payloads, you can implement signature verification in the perform method of your trigger, assuming a key is provided in the subscription response (or as, say, extra info in the oauth token response which you store in a “computed field”).
As for the general question about webhooks and why auth might be different on a hook receive endpoint. I think the answer is in this part of your question: “My app also has its own HTTP API that anyone authorized can call.” In many cases, such as Zapier, you won’t have this 2-way relationship between the producer and the consumer. This is part of what distinguishes what gets called a “web hook” from just a regular API invocation. I have an authenticated relationship to you, to tell you to send me messages. You know it’s me who asked for them and told you where to send the messages. Now you just send messages to that HTTP endpoint. But at no point did you set up a principal and store credentials in my system to use my endpoint. I could tell you to send that message to anyone’s endpoint and you wouldn’t care. This how lots of webhooks work, generally, and it’s that flexibility and ease to set up that made them popular.
The subscription URL should be unique to the trigger instance. It should include a long not-guessable value. It’s transmitted securely. The hook messages should be transmitted securely. Some teams require an extra level of security to ensure that the message actually did come from the sender and hadn’t been tampered with in the middle, and that’s where digest verification or other mechanisms come it.
Another distinguishing feature of “webhooks” vs regular API calls is that most often no user-specific information is transmitted back to the web hook producer from the hook consumer, eliminating the need, in many cases, for both parties to have authenticated relationships with each other.
Disclaimer: Please talk to a security expert when architecting your system to identify adequate measures for the use cases and data you are handling.
Thank you for this thorough answer!
I still have some misunderstanding though.
Ok, if there is no authorization in place when I send messages to you, that is completely clear. This is what makes a webhook different from an API call.
But let’s consider the case when I do set up credentials (actually you asked me to use those) when calling a webhook URL. Let’s say you gave me a token on the settings page on my end and asked me to send it with every request (actually, Webhooks By Zapier allows this setup). Now we have this 2-way relationship as with regular API calls.
So the question still applies. Why is it usually considered not enough? A shared secret can be used directly (the same way as an access token in OAuth2) to authenticate each request. But instead, it is suggested to use it to sign the request with it (using HMAC, for example). Yes, I understand it’s more secure: no secret key transferred across the internet directly. But this logic applies to any request, including a regular API call, doesn’t it?
Teams concerned about security will still want to sign even regular API calls, that is understandable. It just seems to me that there is kind of a bias to use signatures when it comes to webhooks rather than regular API calls and I can’t understand why. Maybe I just got a wrong impression and it’s not the way I described it :)
Ah, I think I just understood something :)
In the case of a regular API call, it’s you who gives me an access token and I use what you gave with the API call. It can still apply in the case of a webhook if we agreed on that. But the more generic approach is that you don’t give me anything except the webhook URL and it’s me who generates the secret token to sign the payload. You may choose to verify the signature or you may skip this step altogether, you decide.
Thanks! You helped me to understand this :)