Webhooks are susceptible to replay attacks


#1

The webhook security is pretty good but I noticed that it could be better in preventing replay attacks.

Anyone who is sniffing traffic can intercept the request and replay it. Granted someone can resolve this by storing processed webhook event ids but this requires the consumer to have a database set up to do this.

To get around this, it would be nice if hubspot added a timestamp for when the webhook was generated. These are added to the header.

Before:

X-HubSpot-Signature: signature

After:

X-HubSpot-Signature: timestamp=1492774577, signature=signature

Side note: Stripe does this and just abbreviates the keys (timestamp, signature) to keep the payload size small.

Now, one can reject the webhook if the timestamp is outside an acceptable threshold (say the last minute).

But what if the attacker just bumps up the timestamp something very recent (say 5 seconds ago)?

The timestamp can be included as a way of computing the signature:

timestamp + client secret + request body

Now if you try to just change the timestamp, the signature verification will fail because it's not valid.


#2

@eric Thanks for the suggestion. This is a great tip.

We're actually in the beginnings of centralizing all of our webhook logic into a single service.
As part of this, we've been looking at the payload and making sure it's up to par.
One of the suggestions our Security team gave was exactly yours: to add a timestap to the message, to eliminate these kinds of attacks.

It will be a while before you'll see them pop up in your webhooks, so keep an eye out for an announcement in a few months.

By the way: Any reason you want the timestamp in the Header vs in the Body?


#3

This is great news @Sean_Duane. I was also going to suggest using an HMAC to compute the signature. I don't understand cryptography enough to explain why but the majority of all webhooks use an HMAC over their hash.

See:

But you would have to release that in a v2 of the webhooks API since it wouldn't be backwards compatible with the way you guys currently have your header set up (unless you just named the header X-Hubspot-Signature-V2).

As for your last question of why it should be in the header instead of the body: it doesn't make sense to make the timestamp so distant from the actual signature. If it fits in the header then why not just include it there? It also doesn't seem semantically correct to be in the body since that's reserved for the webhook data.