Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify X-Hub-Signature from Facebook

I'm something of a beginner with the Play Framework (2.5 and Scala in this case) - and I'm trying to learn by building a bot for Facebook messenger. However I've gotten stuck trying to verify the signature of the messages.

I've followed the Facebook documentation and created a webhook. Which handles POST requests usinggetRawMessages (see code below). This then tries to verify that the request is signed by Facebook using the verifyPayload function. However I can't seem to get the computed and the actual hashes to match up.

I've taken a lead from looking at this question: How to verify Instagram real-time API x-hub-signature in Java? which seems to be doing pretty much what I want, but for the Instagram equivalent. But I still can't seem to get it right.

val secret = "<facebooks secret token>"  

def getRawMessages = Action (parse.raw) {
    request =>

      val xHubSignatureOption = request.headers.get("X-Hub-Signature")

      try {
        for {
          signature <- xHubSignatureOption
          rawBodyAsBytes <- request.body.asBytes()
        } yield {
          val rawBody = rawBodyAsBytes.toArray[Byte]
          val incomingHash = signature.split("=").last
          val verified = verifyPayload(rawBody, secret, incomingHash)
          Logger.info(s"Was verified? $verified")
        }
        Ok("Test")
      }
      catch {
        case _ => Ok("Test")
      }
  }

  val HMAC_SHA1_ALGORITHM = "HmacSHA1"

  def verifyPayload(payloadBytes: Array[Byte], secret: String, expected: String): Boolean =  {

    val secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA1_ALGORITHM)
    val mac = Mac.getInstance(HMAC_SHA1_ALGORITHM)
    mac.init(secretKeySpec)
    val result = mac.doFinal(payloadBytes)

    val computedHash = Hex.encodeHex(result).mkString
    Logger.info(s"Computed hash: $computedHash")

    computedHash == expected
  }

The Facebook webhook docs state:

The HTTP request will contain an X-Hub-Signature header which contains the SHA1 signature of the request payload, using the app secret as the key, and prefixed with sha1=. Your callback endpoint can verify this signature to validate the integrity and origin of the payload

Please note that the calculation is made on the escaped unicode version of the payload, with lower case hex digits. If you just calculate against the decoded bytes, you will end up with a different signature. For example, the string äöå should be escaped to \u00e4\u00f6\u00e5.

I'm guessing that what I'm missing is getting the payload properly escaped to unicode, but I can't seem to find a way to do it. And the answer in the referenced question also appeared to just get the byte array without doing anything more with it (jsonRawBytes = jsonRaw.asBytes();).

Any help with how to proceed would be much appreciated.

like image 670
Johan Avatar asked May 19 '16 19:05

Johan


People also ask

What is XHUB signature?

X-Hub-Signature is a compact way to validate real-time updates, such as webhooks from Facebook and GitHub.

What is a Facebook Webhook?

Webhooks allows you to receive real-time HTTP notifications of changes to specific objects in the Facebook Social Graph. For example, we could send you a notification when any of your app Users change their email address or whenever they comment on your Facebook Page.

Which action do Webhooks enable application to perform?

A webhook is a lightweight API that powers one-way data sharing triggered by events. Together, they enable applications to share data and functionality, and turn the web into something greater than the sum of its parts.


1 Answers

Turns out I was using the wrong secret all along. Should anyone else make the same mistake, please note that it "App Secret" that is available on the application dashboard that you want. See the screenshot below.

enter image description here

like image 131
Johan Avatar answered Oct 16 '22 21:10

Johan