How to Secure Plivo Webhook URLs in Python

Plivo customers control call flows and business logic based on webhook requests Plivo sends to their servers, which also convey critical information such as call records and message status. To keep these webhooks secure, we enable signature validation using hash-based message authentication codes (HMAC-SHA256) so you can check whether the webhook delivered to your server originated from Plivo. This post walks you through how to secure webhooks and authenticate webhook requests using the Plivo Python SDK.

All voice API requests from Plivo’s platform to your application server contain three custom HTTP headers for signature validation: X-Plivo-Signature-V3, X-Plivo-Signature-Ma-V3, and X-Plivo-Signature-V3-Nonce. (For SMS we use X-Plivo-Signature-V2 as of now.) To verify that the request to your server originated from Plivo and that it was not altered en route, you can generate your own request signatures and compare them with the Plivo-Signature headers we pass.

Validating requests on the application server

Plivo Server SDKs include functions to help you validate incoming requests with X-Plivo-Signature HTTP headers. Here’s some sample code that shows how to validate a signature and return an XML element if the webhook is authenticated.

To start, create a working directory and change into it. Within the directory, create a Python virtual environment and activate it, then install the Python libraries we’ll be using.

$ mkdir my-signature-validation && cd my-signature-validation
$ python3 -m venv plivo-env
$ source plivo-env/bin/activate
$ pip install plivo flask

Create a file named signature.py and paste into it this code.

from flask import Flask, request, make_response
import plivo
from plivo import plivoxml

app = Flask(__name__)

@app.route("/speak/", methods=["GET", "POST"])
def validate_signature():
   auth_token = "<auth_token>"
   signature = request.headers.get("X-Plivo-Signature-V3")
   nonce = request.headers.get("X-Plivo-Signature-V3-Nonce")
   webhook_url = request.url
   http_method = request.method

   if http_method == "GET":
       valid_signature = plivo.utils.validate_v3_signature(
           http_method, webhook_url, nonce, auth_token, signature
       )
   elif http_method == "POST":
       params = request.form.to_dict()
       valid_signature = plivo.utils.validate_v3_signature(
           http_method, webhook_url, nonce, auth_token, signature, params
       )
   else:
       return "Method not allowed", 405

   print(f"Your webhook authentication is {valid_signature}")

   # Return an XML answer to Plivo if the signature is valid

   if valid_signature == True:
       xml = plivoxml.ResponseElement()
       speak_params = {"loop": "3"}
       xml.add(plivoxml.SpeakElement("Hello, from Plivo", **speak_params))
       response = make_response(xml.to_string())
       response.headers["Content-type"] = "text/xml"
       print("Send XML to Plivo server")
       print(xml.to_string())
       return response, 200
   else:
       return "Bad request", 400


if __name__ == "__main__":
   app.run(host="0.0.0.0", debug=True)

When someone makes an outbound call to a destination number, Plivo requests the webhook associated with this code. The code runs and returns a Speak XML element if it can authenticate the request sent. If the validation fails the call will terminate, since no XML is returned to the Plivo server.

Run the file using the command python signature.py to initialize the server locally.

terminal

When you’re satisfied, make the application publicly available with ngrok. To test it, make a call to the API using Postman and use the ngrok URL as the answer URL.

postman

Stay secure

Be sure to verify your webhooks using Plivo’s Python SDK and avoid causing any harm to your servers. You can learn more about signature validation logics for voice and messaging.

By the way, signature validation isn’t the only technique we use to keep webhooks secure. All webhook communications run over HTTPS, which are secured by SSL certificates on your server. We also provide a list of IP addresses for our voice API from which you can expect callbacks. You can whitelist these addresses in your infrastructure.

Haven’t tried Plivo yet? Getting started is easy and only takes minutes. Sign up today.

comments powered by Disqus

By submitting this form, you agree we may contact you in the manner described in our Privacy Policy.