Back to Blog

Security · Engineering

Webhook secrets in a multi-tenant integration

A callback URL is a public door. The secret is the lock. The tenant id is which room you may enter.

I merged a Mailchimp Marketing integration into corsairdev/corsair. Fifty-six endpoints. Four webhook triggers. OAuth 2.0 and API-key auth. Fifty-six unit tests and guarded integration tests. The PR is public: #351.

The security-relevant part is not the endpoint count. It is what happens when Mailchimp calls you back.

Anyone can POST your webhook URL

If you accept the body because the path looks right, you have built an unauthenticated write API. Signature or secret checks come first. Then you parse. Then you route.

Tenants do not share a hallway

Multi-tenant routing means the event for workspace A cannot land in workspace B’s automations. The secret proves Mailchimp sent it. The tenant mapping proves which customer owns the list. Mix those up and you have a quiet data leak.

Tests that block the merge

Unit tests cover the dull cases: bad secret, missing header, unknown tenant. Integration tests stay guarded so CI does not need live Mailchimp on every push. That is enough to keep a regression from shipping as “just a mapping change.”

A webhook without a secret is a public function with side effects.

How I’d attack it

Where this sits

This is secure API hygiene on an open-source integration. It’s evidence I think about auth, secrets, and tenant boundaries, and it’s the kind of surface I now look at from the attacker’s side.

Related

Introduction