pythondjangoapiwebhookspostmark

Postmark Webhook Signature Security


https://postmarkapp.com/developer/webhooks/webhooks-overview

https://<username>:<password>@example.com/webhook

Looking through the Postmark API, there does not seem to be any information about webhook signatures or tokens or hmac for security. The only security mentioned is basic authentication or firewalls.

How do you even set up basic authentication or firewalls to work with Postmark webhooks? Is there anything that needs to be done on nginx or apache?

Basic authentication like this?

requests.post('url',headers=headers,auth=('username','password'), json=json_data)

username:password@mysite.com

To me that does not seem as secure than signature verification. Other APIs like Mailgun have signatures and tokens and hmac for verifying webhooks in the request header.

Mailgun Example:

https://documentation.mailgun.com/en/latest/user_manual.html#webhooks-1

import hashlib, hmac, json
def verify(signing_key, token, timestamp, signature):
    hmac_digest = hmac.new(key=signing_key.encode(),
                           msg=('{}{}'.format(timestamp, token)).encode(),
                           digestmod=hashlib.sha256).hexdigest()
    return hmac.compare_digest(str(signature), str(hmac_digest))


@csrf_exempt
def mailgun_webhook(request):
  body_unicode = request.body.decode('utf-8')
  body = json.loads(body_unicode)
  signature = body['signature']['signature']
  token = body['signature']['token']
  timestamp = body['signature']['timestamp']
  webhook_signing_key = 'KEY'
  if verify(webhook_signing_key, token, timestamp, signature) is True:
    print('do something')
  return HttpResponse(status=200)

Solution

  • You are correct Postmark only supports basic authentication. And they further suggest the use of firewall rules since they can provide you with static IPs to add to the allow list.

    Let's explore how to do it with firewall rules and basic auth with Nginx.

    Basic Auth

    You can implement basic auth in your application server or web server.

    For nginx you can find documentation on the topic here https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/.

    Your nginx configuration might look like this:

    server {
        ...
    
        location /webhooks {
            auth_basic           "Webhook Area";
            auth_basic_user_file /etc/apache2/.htpasswd; 
            ...
        }
    }
    

    And at Postmark you create a webhook endpoint following their suggested format: https://<username>:<password>@yoursite.com/webhooks where the username and password match what is found in the .htpasswd file.

    Firewall

    This option is only possible because Postmark guarantees that its webhooks will be sent from a set of Static IPs.

    In terms of implementation, firewalling varies greatly with what sort of infrastructure you are running. Is there a dedicated app that is tasked mainly with handling webhooks? Do you have a web server (nginx) proxying? Are you using WAF?

    To use Nginx as an example again following the same link provided in the previous section you can find a section talking about "Access Restriction by IP Address".

    Which can look like:

    server {
        ...
    
        location /webhooks {
            #...
            allow 3.134.147.250;
            allow 50.31.156.6;
            allow 50.31.156.77;
            allow 18.217.206.57;
            deny  all;
            ...
        }
    }
    

    You can consult Postmarks documentation for an up-to-date list of IPs https://postmarkapp.com/support/article/800-ips-for-firewalls#webhooks.

    If you are using a WAF they should also have support for IP-based rules e.g. Cloudflare WAF https://developers.cloudflare.com/waf/tools/ip-access-rules/.