2

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.

Beau
  • 201
  • 3
  • 4
  • I guess that [HMAC](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) is a standard, have you searched for a PHP implementation? – halfer Jan 09 '18 at 22:06
  • Hmm, it looks like [it's in the PHP core](https://duckduckgo.com/?q=php+hmac). – halfer Jan 09 '18 at 22:08
  • 1
    @Beau, so what you need to do is something like: `$mysig = base64_encode(hash_hmac('sha1', $raw, "$client_id&$client_secret"))` to generate the signature, and then check it matches what is returned in the header `$mysig == $api_sig` where `$api_sig` is the signature from the header. The `$client_id` and `$client_secret` values should be pulled from your own configs/storage which you get from the developer portal (developer.surveymonkey.com). – General Kandalaft Jan 11 '18 at 19:33
  • 1
    @GeneralKandalaft, thank you SO much for taking the time to read the details of my problem and to offer a legitimate suggestion. While the code you provided didn't exactly achieve what I needed, it did clarify enough information that I was able to arrive at the solution, which I've added in an edit. THANK YOU SO MUCH! – Beau Jan 12 '18 at 05:09
  • The information in this post is helpful. It is not exactly a duplicate since the information is unique to working with Survey Monkey webhooks. – ken-mills May 12 '19 at 19:27

0 Answers0