How to ensure HTTP requests are coming from the Meya Bot Platform

In order to ensure that requests are coming from Meya Bot Platform and not a malicious third party, we have included a cryptographic signature.

This works very similar to how Twilio security works.

How Meya signs requests to your server

  1. Meya appends JSON body /w sorted keys, and spaces removed to the end of the URL (with no delimiter)
  2. Meya takes the resulting string (the full URL with query string and the raw body) and signs it using HMAC-SHA1 and your api_key as the key.
  3. Meya sends this signature in an HTTP header called X-Meya-Signature

How to verify the signature

  1. Take the full URL of the request and append the JSON body /w sorted keys, and spaces removed as a string
  2. Sign the resulting string with HMAC-SHA1 using your api_key as the key
  3. Base64 encode the resulting hash value
  4. Compare your hash to ours, submitted in the X-Meya-Signature header. If they match, then you've verified that Meya is the originator

Example headers

POST /yourpath HTTP/1.1
Host: yourdomain.com
Accept-Encoding: gzip, deflate
Accept: application/json
User-Agent: python-requests/2.13.0
X-Meya-Signature: Wrl+9YX4ok2RI0VAugSwKcVWgto=
Content-Type: application/json
Content-Length: 130
X-Forwarded-Proto: https
X-Forwarded-For: 52.91.53.134

How to verify signature

from hashlib import sha1
import hmac
import json

API_KEY = "a6xE5yLe0RHPYbiHqR3OKDmRgsZ76RY3"

"""
Verify with the following data:
url = "https://203f31bb.ngrok.io/rx/apibot"
body = '{"user_id": "1234567890", "sender": "user", "timestamp": 1489843997.625053, "text": "hi", "type": "text", "bot_id": "BJty7evfw2b"}'
signature = "Wrl+9YX4ok2RI0VAugSwKcVWgto="
"""

def standardize_json_string(raw_body):
  # ensure sorted keys and no separators
  data = json.loads(raw_body)
  return json.dumps(data, sort_keys=True, separators=(',', ':'))


def validate(request):
	url = request.url
	message = standardize_json_string(request.body)
  signature = request.headers.get('X-Meya-Signature')

  data = str(url + message)
  hashed = hmac.new(API_KEY, data, sha1)
  computed_signature = hashed.digest().encode("base64").rstrip('\n')

  return computed_signature == signature