I've successfully implemented a webhook in my PHP application, but am unsure on how to verify the Sm-Signature
header.
The documentation here states:
Our webhook callbacks are sent as POST requests and include the following request headers that can be used to verify the request is coming from SurveyMonkey:
Sm-Apikey: Your Client ID or API key (if you’re using OLD Authentication) Sm-Signature: # HMAC digest hashed with sha1
The only documentation that I could find that explains how this is done is written in Python, which I am not familiar with at all. The Python code offered is:
import hashlib
import hmac
import base64
def generate_signature(payload, api_key, api_secret):
"""
:type payload: str The response body from the webhook exactly as you received it
:type api_key: str Your API Key for you app (if you have one) otherwise your Client ID
:type api_secret: str Your API Secret for your app
:return: str
"""
# ensure all strings passed in are ascii strings,
# as hmac does not work on unicode
api_key = api_key.encode("ascii")
api_secret = api_secret.encode("ascii")
payload = payload.encode("ascii")
signature = hmac.new(
key='%s&%s' % (api_key, api_secret),
msg=payload,
digestmod=hashlib.sha1)
signature_digest = signature.digest()
return base64.b64encode(signature_digest)
# Compare the signature generated from the request body against the one in the request headers
# Where "request" is the HTTP request received for the event, attributes to access body/headers may vary by framework
hmac.compare_digest(generate_signature(request.body, api_key, api_secret), request.headers['Sm-Signature'])
In addition to not being Python savvy, I'm also not very familiar with PHP's hashing, digest and other cryptographic functions. How can I interpret this into PHP and/or explain how this verification would be performed in PHP?
Edit: I appreciate the effort to point me in the right direction, but I've tried that code, along with code from several other answers, and none produced the result that I need. Furthermore, I'm asking in the specific context of SurveyMonkey. There is no documentation for a PHP implementation of SurveyMonkey's v3 webhook verification that I can find. This is not a duplicate, as my question is specifically how to perform this verification.
So far, I have tried all of these variations, where $raw
is the unparsed POST message body, $api_sig
is the Sm-Signature
header value, $api_key
is the key passed as the Sm-Apikey
header and API_CLIENT_ID
is my client ID (which actually is identical to the Sm-Apikey
).
// Various combinations, each with and without base64 encoding.
hash_hmac('sha1', $raw, $api_key)
base64_encode(hash_hmac('sha1', $raw, $api_key))
hash_hmac('sha1', $raw, $api_sig)
base64_encode(hash_hmac('sha1', $raw, $api_sig))
hash_hmac('sha1', $raw, API_CLIENT_ID)
base64_encode(hash_hmac('sha1', $raw, API_CLIENT_ID))
hash_hmac('sha1', $raw, "$api_key&$api_sig")
base64_encode(hash_hmac('sha1', $raw, "$api_key&$api_sig"))
hash_hmac('sha1', $raw, API_CLIENT_ID."&$api_sig")
base64_encode(hash_hmac('sha1', $raw, API_CLIENT_ID."&$api_sig"))
// This time with raw outputs.
hash_hmac('sha1', $raw, $api_key, TRUE)
base64_encode(hash_hmac('sha1', $raw, $api_key, TRUE))
hash_hmac('sha1', $raw, $api_sig, TRUE)
base64_encode(hash_hmac('sha1', $raw, $api_sig, TRUE))
hash_hmac('sha1', $raw, API_CLIENT_ID, TRUE)
base64_encode(hash_hmac('sha1', $raw, API_CLIENT_ID, TRUE))
hash_hmac('sha1', $raw, "$api_key&$api_sig", TRUE)
base64_encode(hash_hmac('sha1', $raw, "$api_key&$api_sig", TRUE))
hash_hmac('sha1', $raw, API_CLIENT_ID."&$api_sig", TRUE)
base64_encode(hash_hmac('sha1', $raw, API_CLIENT_ID."&$api_sig", TRUE))
None of these produced a value matching Sm-Signature
, and all of the outputs were about 2 - 3 times the length of Sm-Signature
. I suspect that another encoding is required (before and/or after the HMAC digest), but I'm not knowledgeable enough to know what it would be.
Edit 2: Thanks to General Kandalaft's sample code, I was able to find the solution. I didn't have a grasp on what data to pass as the key, but his suggestion helped. This, combined with information I had come across while researching this issue, allowed me to successfully perform the verification. For those looking for this answer, it is:
base64_encode(hex2bin(hash_hmac('sha1', $raw, API_CLIENT_ID.'&'.API_SECRET)));
I would appreciate it if an admin could remove the duplicate tag so that I can post the solution as a proper answer.