1
votes

I'm trying to verify the signature on facebook signed_requests (https://developers.facebook.com/docs/reference/login/signed-request/), using python 3.6. So far I've tried:

  • parse_signed_request() on the facebook python sdk here https://facebook-sdk.readthedocs.io
  • this approach facebook signed request email
  • this too http://sunilarora.org/parsing-signedrequest-parameter-in-python-bas/
  • using python 2.7 and 3.2
  • using the python cryptography library like so

    from cryptography.hazmat.primitives import hashes, hmac from cryptography.hazmat.backends import default_backend

    h = hmac.HMAC(key, hashes.SHA256(), backend=default_backend()) h.update(message) signature = h.finalize()

  • replacing -_ chars with +/ a la Facebook signed_request Invalid via a simple replace("-", "+").replace("_", "/"). I had hopes for this one but I can't seem to verify a sig even if it lacks any of the above symbols like this one for example GlU46mFitdvtOnm56aLR3xQ1RUBzoCY0k2u1kMjBdQA.ey[...] ..so.. yeah..

  • I've attempted different string encodings (utf-8 / ascii) on the signature / payload

  • different hashing algorithms (sha512 / md5 ..)
  • hashing the base64 strings over decoding them from base64 first
  • regenerating my app secret (I've double checked that the app ID that appears in the payload references the correct app, and thus secret)

So far I've been unable to produce the same signature that comes on given signed request(s) .. so every facebook message is considered invalid (less than ideal!).

How on earth does facebook generate these signatures? How do I generate them correctly (or incorrectly, if it matches facebook :P I'm not fussy) in order to validate them?


Edit: Thanks for the comment. I've read that link too .. it doesn't seem to help work this out though. Here's a step by step with example -

> import json
> import base64
> from cryptography.fernet import Fernet
> from cryptography.hazmat.backends import default_backend
> from cryptography.hazmat.primitives import hashes, hmac, padding
> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

# app secret copied straight from developer.facebook.com -> settings -> basic
> secrect = 'obviouslythisisnttherealone'

# signed_request from facebook
> r = 'xik7puSFmT1d1LNBlqFVTOCb8TiH0Rg1xBfr7zzf9rg.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImNvbW11bml0eV9pZCI6bnVsbCwiaXNzdWVkX2F0IjoxNTI0NTU4NjcxLCJtZXRhZGF0YSI6bnVsbCwicGFnZV9pZCI6MTYxMjU3Njk0NjE4NjAxLCJwc2lkIjoiMTQ2ODkzNzEwNjU2MTA5NSIsInRocmVhZF90eXBlIjoiVVNFUl9UT19QQUdFIiwidGlkIjoiMTQ2ODkzNzEwNjU2MTA5NSJ9'

# spilt on '.'
> b64_sig, b64_data = r.split('.')
> print(b64_sig, b64_data)
xik7puSFmT1d1LNBlqFVTOCb8TiH0Rg1xBfr7zzf9rg eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImNvbW11bml0eV9pZCI6bnVsbCwiaXNzdWVkX2F0IjoxNTI0NTU4NjcxLCJtZXRhZGF0YSI6bnVsbCwicGFnZV9pZCI6MTYxMjU3Njk0NjE4NjAxLCJwc2lkIjoiMTQ2ODkzNzEwNjU2MTA5NSIsInRocmVhZF90eXBlIjoiVVNFUl9UT19QQUdFIiwidGlkIjoiMTQ2ODkzNzEwNjU2MTA5NSJ9

# there are no - or _ chars to replace in this so ..
> decoded_data = base64.b64decode(b64_data)
> decoded_sig = base64.b64decode(b64_sig + "=")  # add another '=' to pad out the base64 encoding
> print (decoded_data, decoded_sig)
b'{"algorithm":"HMAC-SHA256","community_id":null,"issued_at":1524558671,"metadata":null,"page_id":161257694618601,"psid":"1468937106561095","thread_type":"USER_TO_PAGE","tid":"1468937106561095"}' b'\xc6);\xa6\xe4\x85\x99=]\xd4\xb3A\x96\xa1UL\xe0\x9b\xf18\x87\xd1\x185\xc4\x17\xeb\xef<\xdf\xf6\xb8'

# encode secret into bytes
> bytes_secret = bytes(secret, encoding='ascii')

> h = hmac.HMAC(bytes_secret, hashes.SHA256(), backend=default_backend())
> h.update(decoded_data)
> h.finalize()
b"\xe9\x0f\xbf\xee\xef\xc8\xf0\x96'Im\x1a@\x9d\xc7S\x82%\xe4<\xa0\xbc\xff\x93\xcb\x11~\x7fv\x90\x9f\xb6"
#  ^ wrong

# .. uh .. perhaps in the reverse order?
> h = hmac.HMAC(decoded_data, hashes.SHA256(), backend=default_backend())
> h.update(bytes_secret)
> h.finalize()
b'fnc\xceu\xf8\xf7\xda\xc4\xfe\xca\xe2\x1b\x8b\x00\x9en\xbe\xc7a\xf0P\x9a\\\xfd\xaad\xcb[}E='
# ^ wrong

# ok fine, let's try using a different hashing tool
> import binascii
> hex_sig = binascii.hexlify(decoded_sig)

# the sig we want, in hex
>  print(hex_sig)
b'c6293ba6e485993d5dd4b34196a1554ce09bf13887d11835c417ebef3cdff6b8'

> from hashlib import sha256
> h = sha256(bytes_secret)
> h.update(decoded_data)
> print(h.hexdigest())
'54acbc303f4d85831e4bb9f4818a233e71e7f7c4eef4a585c61ed70f7cf1e07f'
# ^ wrong

# uh ..
> h = sha256(bytes_secret + decoded_data)
> print(h.hexdigest())
'54acbc303f4d85831e4bb9f4818a233e71e7f7c4eef4a585c61ed70f7cf1e07f'
# ^ wrong

# using 2.0.0 facebook python sdk
> import facebook
> facebook.parse_signed_request(r, secret)
False

None of these approaches work (and this isn't everything I've tried) ..

1

1 Answers

1
votes

Update: Hopefully this proves helpful to others hunting for something similar - the signature generation functions were correct, there was a bug in something hard coding the (wrong) app id hidden in some of front end code.