Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Trello API: Trello webhook signatures and Python

Ethan Kiczek March 6, 2018

Hi Trello,

I’m trying to write some python code inside of an AWS Lambda function to verify the webhook signature from Trello. I’ve tried to follow the directions at https://developers.trello.com/page/webhooks#section-webhook-signatures but I can’t seem to get this working. Here’s what I have:

import os

import hmac

import hashlib

import base64

 

def handler(event, context):

   header = event['headers']['x-trello-webhook']

   sha1_check = base64.b64encode(hmac.new(<MY_TRELLO_API_KEY_TOKEN>, event['body'] + <MY_TRELLO_CALLBACK_URL>, hashlib.sha1).digest())

My code is syntactically correct, but it’s not working. When I attempt to compare header and sha1_check, they are not equal. I confirmed this by echoing their values to the logs. The docs at https://developers.trello.com/page/webhooks#section-webhook-signatures say:

Each webhook trigger contains the HTTP header X-Trello-Webhook. The header is a base64 digest of an HMAC-SHA1 hash. The hashed content is the concatenation of the full request body and the callbackURL exactly as it was provided during webhook creation. The key used to sign this text is your application’s secret.

I think my code takes all of this into account.

Can you help me figure out what is wrong?

Thanks,

Ethan

 

4 answers

1 accepted

1 vote
Answer accepted
Ethan Kiczek March 7, 2018

SOLVED: Emailed with Bentley Cook, a Developer Advocate, and discovered that the string you pass in to generate the HMAC is not a token that has been generated, but, instead it is the OAuth application secret that you will find at the bottom of the page at https://trello.com/app-key.

 

Once I used that as the secret... it worked!

bentley
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
March 8, 2018

👍Awesome!

Would you mind posting the final code snippet that worked? I'd like to add it to that webhooks page as an example on how you'd check signatures in Python.

Ethan Kiczek March 8, 2018

I'm happy to share the code!

I also lock down the originating IP to the Trello API IPs (I can't remember where I got that code from, but it's out there on Google), I'm including that code as well:

authorized_ips = """
107.23.104.115
107.23.149.70
54.152.166.250
54.164.77.56
54.209.149.230
"""

def is_authorized_ip(ip):
ips = set()
for x in authorized_ips.strip().split("\n"):
ips.add(x.split('#')[0].strip())

return ip in ips

def base64Digest(secret):
mac = hmac.new(os.environ['TRELLO_OAUTH_SECRET'], secret, hashlib.sha1)
return base64.b64encode(mac.digest())

def is_from_trello(hashed_header, request_body):
logger.info('hashed_header: ' + hashed_header)

double_hashed_header = base64Digest(hashed_header)
logger.info('double_hashed_header: ' + double_hashed_header)

double_hashed_body = base64Digest(base64Digest(request_body + os.environ['TRELLO_CALLBACK_URL']))
logger.info('double_hashed_body: ' + double_hashed_body)

return hmac.compare_digest(double_hashed_header, double_hashed_body)

def handler(event, context):
logger.info('got event{}'.format(event))

if not is_authorized_ip(event['requestContext']['identity']['sourceIp']):
logger.info('Unauthorized IP: ' + event['requestContext']['identity']['sourceIp'])
return { 'statusCode': 403, 'body': 'IP Deny' }

if not is_from_trello(event['headers']['x-trello-webhook'], event['body']):
logger.info('Invalid request signature, denying request')
logger.info('Headers:' + json.dumps(event['headers']))
return { 'statusCode': 403, 'body': 'Invalid request signature' }

Like ckraft likes this
0 votes
javmah December 4, 2023

How to Validate Trello Webhook Signatures in Golang ? I Tried many ways to verify the webhook in Golang Fiber, But all times calculated Hmac was different than the header x-trello-webhook. Could anyone kindly please share a working code? Thank you so so much.

0 votes
Denis Stebunov
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
May 10, 2020

The examples in this thread are invalid. You don't need to double-hash the signature that comes in headers. Here's a working example for Django/Python3:

import hashlib
import hmac
from base64 import b64encode


TRELLO_SECRET = 'your-secret-here'


def webhook_signature_valid(request):
signature = request.META.get('X_TRELLO_WEBHOOK')
if not signature:
return False
msg = request.body + request.build_absolute_uri().encode()
body_digest = b64encode(hmac.new(
key=TRELLO_SECRET.encode(),
msg=msg,
digestmod=hashlib.sha1,
).digest())
return hmac.compare_digest(signature.encode(), body_digest)
0 votes
jeff schwaber January 12, 2020

Hi! I'm attempting to make this answer work with Python3, and having bytes problems. Here's the code I've gotten to that no longer syntax errors, but it's hard to tell why it's failing now, as all that I can see now is that the hashes are different.

import hashlib, hmac, base64

# tried both, neither works
#encoding = 'ascii'
encoding = 'utf-8'
TRELLO_SECRET = '' # filled this in
webhook_url = '' # filled this in

def base64Digest(secret):
key = TRELLO_SECRET.encode(encoding)
if isinstance(secret, str):
secret = secret.encode(encoding)
mac = hmac.new(key, secret, hashlib.sha1)
return base64.b64encode(mac.digest())


def is_from_trello(hashed_header, request_body):
print('hashed_header: ', hashed_header)

double_hashed_header = base64Digest(hashed_header)
print('double_hashed_header: ', double_hashed_header)

double_hashed_body = base64Digest(
base64Digest(request_body + webhook_url.encode(encoding)))
print('double_hashed_body: ', double_hashed_body)

return hmac.compare_digest(double_hashed_header, double_hashed_body)

jeff schwaber January 12, 2020

@bentleydo you have any suggestions?

jeff schwaber January 12, 2020

ah ha! That does work, I had a tiny typo in my webhook_url. Well, there's a python3 example, for the future!

Like bentley likes this
bentley
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
January 13, 2020

For any future folks that end up here, I'd also recommend asking questions over on the Trello developers' forum: https://community.developer.atlassian.com/c/trello. It has a much higher number of engaged developers building applications on top of Trello.

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events