4

I am trying to validate an incoming webhook and so far the resulting hash is not matching the test hash generated by the api.

The docs list the following example for Ruby however I am using Python/Django so any help to 'convert' this function would be appreciated!

Ruby Function

# request_signature - the signature sent in Webhook-Signature
#      request_body - the JSON body of the webhook request
#            secret - the secret for the webhook endpoint

require "openssl"

digest = OpenSSL::Digest.new("sha256")
calculated_signature = OpenSSL::HMAC.hexdigest(digest, secret, request_body)

if calculated_signature == request_signature
  # Signature ok!
else
  # Invalid signature. Ignore the webhook and return 498 Token Invalid
end

This is roughly what I have put together myself so far using https://docs.python.org/3/library/hashlib.html.

Python Attempt

import hashlib

secret = "xxxxxxxxxxxxxxxxxx"
json_data = {json data}

h = hashlib.new('sha256')
h.update(secret)
h.update(str(json_data))
calculated_signature = h.hexdigest()

if calculated_signature == webhook_signature:
    do_something()
else:
    return 498

When I run the above the hashes never match obviously due to my incorrect Python implementation.

Any help/pointers would be greatly appreciated!

Community
  • 1
  • 1
Karl
  • 733
  • 1
  • 14
  • 28

1 Answers1

9

I believe it should be something like this:

import hmac
import hashlib
digester = hmac.new(secret, request_body, hashlib.sha256)
calculated_signature = digester.hexdigest()

A few notes:

  1. Use the actual request body. Don't rely on str(json_data) equalling the request body. This will almost certainly fail as python will print out inner strings using repr which will likely leave a bunch of spurious u"..." that aren't actually in the response. json.dumps won't necessarily do better because there could be whitespace differences that are isignificant to JSON, but are very significant to the hmac signature.
  2. hmac is your friend :-)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Thanks for the quick answer, ill give this a spin and let you know how I get on! – Karl Feb 18 '16 at 21:00
  • Hi, I have just got round to testing this however it does not like the json body. It gives a "TypeError: must be string or buffer, not dict" if i dont pass it as a string which gives an incorrect hash. – Karl Feb 22 '16 at 11:39
  • 1
    When I said the request body, I meant the _actual_ request body -- Not the decoded json from the request body. In typical circumstances your web framework will probably "hide" the body and just give you the decoded JSON (it's probably not actually hidden, just not commonly used...). I'm not a django user, so I don't know how to access the "raw" data that you're looking for... How are you getting the request body from the request? – mgilson Feb 22 '16 at 11:58
  • 1
    That was the problem, I was passing the json data, not request.body. Works a treat now, Thanks! – Karl Feb 22 '16 at 16:37
  • how are you getting request.body? I tried `requests.data` but still not getting the correct hmac... – AK_ Dec 24 '20 at 18:17