Docs: clean up "securing webhooks"

* "SSL" --> "https"
* "authorization" --> "authentication"
  (e.g., "HTTP basic authentication" -- except when referring
  specifically to the HTTP "Authorization" header used to send it)
* add a sidebar with more details on why it matters
This commit is contained in:
medmunds
2018-03-07 12:19:38 -08:00
parent e3f986df8f
commit ae8484fd65
2 changed files with 48 additions and 19 deletions

View File

@@ -269,14 +269,14 @@ Set to `True` to ignore these problems and send the email anyway. See
.. rubric:: WEBHOOK_SECRET .. rubric:: WEBHOOK_SECRET
A `'random:random'` shared secret string. Anymail will reject incoming webhook calls A `'random:random'` shared secret string. Anymail will reject incoming webhook calls
from your ESP that don't include this authorization. You can also give a list of from your ESP that don't include this authentication. You can also give a list of
shared secret strings, and Anymail will allow ESP webhook calls that match any of them shared secret strings, and Anymail will allow ESP webhook calls that match any of them
(to facilitate credential rotation). See :ref:`securing-webhooks`. (to facilitate credential rotation). See :ref:`securing-webhooks`.
Default is unset, which leaves your webhooks insecure. Anymail Default is unset, which leaves your webhooks insecure. Anymail
will warn if you try to use webhooks without a shared secret. will warn if you try to use webhooks without a shared secret.
This is actually implemented using HTTP basic authorization, and the string is This is actually implemented using HTTP basic authentication, and the string is
technically a "username:password" format. But you should *not* use any real technically a "username:password" format. But you should *not* use any real
username or password for this shared secret. username or password for this shared secret.

View File

@@ -6,15 +6,37 @@ Securing webhooks
If not used carefully, webhooks can create security vulnerabilities If not used carefully, webhooks can create security vulnerabilities
in your Django application. in your Django application.
At minimum, you should **use SSL** and a **shared authorization secret** At minimum, you should **use https** and a **shared authentication secret**
for your Anymail webhooks. (Really, for *any* webhooks.) for your Anymail webhooks. (Really, for *any* webhooks.)
Use SSL .. sidebar:: Does this really matter?
-------
Your Django site must use SSL, and the webhook URLs you Short answer: yes!
give your ESP should start with "https" (not http).
Do you allow unauthorized access to your APIs? Would you want
someone eavesdropping on API calls? Of course not. Well, a webhook
is just another API.
Think about the data your ESP sends and what your app does with it.
If your webhooks aren't secured, an attacker could...
* accumulate a list of your customers' email addresses
* fake bounces and spam reports, so you block valid user emails
* see the full contents of email from your users
* convincingly forge incoming mail, tricking your app into publishing
spam or acting on falsified commands
* overwhelm your DB with garbage data (do you store tracking info?
incoming attachments?)
... or worse. Why take a chance?
Use https
---------
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. Without https, the data your ESP sends your webhooks is exposed in transit.
This can include your customers' email addresses, the contents of messages This can include your customers' email addresses, the contents of messages
@@ -22,24 +44,29 @@ 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 to your webhooks (described in the next section), and other data you'd
probably like to keep private. probably like to keep private.
Configuring SSL is beyond the scope of Anymail, but there are many good Configuring https is beyond the scope of Anymail, but there are many good
tutorials on the web. 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 If you aren't able to use https on your Django site, then you should
not set up your ESP's webhooks. not set up your ESP's webhooks.
.. _Let's Encrypt: https://letsencrypt.org/
.. setting:: ANYMAIL_WEBHOOK_SECRET .. setting:: ANYMAIL_WEBHOOK_SECRET
Use a shared authorization secret Use a shared authentication secret
--------------------------------- ----------------------------------
A webhook is an ordinary URL---anyone can post anything to it. A webhook is an ordinary URL---anyone can post anything to it.
To avoid receiving random (or malicious) data in your webhook, To avoid receiving random (or malicious) data in your webhook,
you should use a shared random secret that your ESP can present you should use a shared random secret that your ESP can present
with webhook data, to prove the post is coming from your ESP. with webhook data, to prove the post is coming from your ESP.
Most ESPs recommend using HTTP basic authorization as this shared Most ESPs recommend using HTTP basic authentication as this shared
secret. Anymail includes support for this, via the secret. Anymail includes support for this, via the
:setting:`!ANYMAIL_WEBHOOK_SECRET` setting. :setting:`!ANYMAIL_WEBHOOK_SECRET` setting.
Basic usage is covered in the Basic usage is covered in the
@@ -53,8 +80,8 @@ This will result in an HTTP 400 response, without further processing
the data or calling your signal receiver function. the data or calling your signal receiver function.
In addition to a single "random:random" string, you can give a list In addition to a single "random:random" string, you can give a list
of authorization strings. Anymail will permit webhook calls that match of authentication strings. Anymail will permit webhook calls that match
any of the authorization strings: any of the authentication strings:
.. code-block:: python .. code-block:: python
@@ -66,14 +93,14 @@ any of the authorization strings:
], ],
} }
This facilitates credential rotation: first, append a new authorization This facilitates credential rotation: first, append a new authentication
string to the list, and deploy your Django site. Then, update the webhook string to the list, and deploy your Django site. Then, update the webhook
URLs at your ESP to use the new authorization. Finally, remove the old URLs at your ESP to use the new authentication. Finally, remove the old
(now unused) authorization string from the list and re-deploy. (now unused) authentication string from the list and re-deploy.
.. warning:: .. warning::
If your webhook URLs don't use https, this shared authorization If your webhook URLs don't use https, this shared authentication
secret won't stay secret, defeating its purpose. secret won't stay secret, defeating its purpose.
@@ -105,5 +132,7 @@ For example, you might consider:
* Configuring your firewall to reject webhook calls that come from * Configuring your firewall to reject webhook calls that come from
somewhere other than your ESP's documented IP addresses (if your ESP somewhere other than your ESP's documented IP addresses (if your ESP
provides this information) provides this information)
* Rate-limiting webhook calls in your web server or using something
like :pypi:`django-ratelimit`
But you should start with using SSL and a random shared secret via HTTP auth. But you should start with using https and a random shared secret via HTTP auth.