If not used carefully, webhooks can create security vulnerabilities in your Django application.
At minimum, you should use https and a shared authentication secret for your Anymail webhooks. (Really, for any webhooks.)
For security, your Django site must use https. The webhook URLs you give your ESP need to start with https (not http).
Without https, the data your ESP sends your webhooks is exposed in transit. This can include your customers’ email addresses, the contents of messages you receive through your ESP, the shared secret used to authorize calls to your webhooks (described in the next section), and other data you’d probably like to keep private.
Configuring https is beyond the scope of Anymail, but there are many good tutorials on the web. If you’ve previously dismissed https as too expensive or too complicated, please take another look. Free https certificates are available from Let’s Encrypt, and many hosting providers now offer easy https configuration using Let’s Encrypt or their own no-cost option.
If you aren’t able to use https on your Django site, then you should not set up your ESP’s webhooks.
Some ESPs implement webhook signing, which is another method of verifying the webhook data came from your ESP. Anymail will verify these signatures for ESPs that support them. See the docs for your specific ESP for more details and configuration that may be required.
Even with signed webhooks, it doesn’t hurt to also use a shared secret.
Webhooks aren’t unique to Anymail or to ESPs. They’re used for many different types of inter-site communication, and you can find additional recommendations for improving webhook security on the web.
For example, you might consider:
event_id, to avoid accidental double-processing of the same events (or replay attacks)
- Checking the webhook’s
timestampis reasonably close the current time
- Configuring your firewall to reject webhook calls that come from somewhere other than your ESP’s documented IP addresses (if your ESP provides this information)
- Rate-limiting webhook calls in your web server or using something like django-ratelimit
But you should start with using https and a random shared secret via HTTP auth.