Hey everyone,
I’m trying to secure our internal WFM dashboard that receives Genesys Cloud event webhooks. We’ve been getting hit by some weird replay attacks where old events are being resent and messing up our adherence calculations. I need to verify the signature header to make sure the payload is fresh and hasn’t been tampered with.
I’m using Python with Flask to handle the incoming POST requests. The documentation mentions checking the X-Genesys-Signature header, but I’m not sure if I’m constructing the payload string correctly for the HMAC calculation.
Here is the relevant snippet from my route handler:
import hmac
import hashlib
import os
@app.route('/webhook/event', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Genesys-Signature')
timestamp = request.headers.get('X-Genesys-Timestamp')
body = request.get_data(as_text=True)
# My secret from the Genesys Cloud webhook config
secret = os.environ.get('GENESYS_WEBHOOK_SECRET')
# Constructing the string to sign
# Not sure if I should include the timestamp or just the body?
string_to_sign = timestamp + body
expected_signature = hmac.new(
secret.encode('utf-8'),
string_to_sign.encode('utf-8'),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
return "Signature mismatch", 401
return "OK", 200
The issue is that hmac.compare_digest is always returning False. I’ve double-checked the secret key and it matches what’s in the Genesys Cloud integration settings. The timestamp header seems to be present, but I’m guessing I might be formatting the string_to_sign wrong. Should I be hashing just the body? Or is there a specific format for combining the timestamp and payload that I’m missing?
Any help would be appreciated. I don’t want to leave this endpoint open to replay issues while I figure this out.