Securing Inbound SMS Webhooks

Overview

As a best practice, we recommend securing your inbound SMS webhooks to ensure that they are being triggered by IntelePeer and not a third party. There are two ways you can secure your webhooks, which are not mutually exclusive:

  • Securing inbound SMS through basic authentication
  • Validating that an inbound SMS originates from IntelePeer

Securing inbound SMS through basic authentication

The first way to secure your inbound webhook is entirely in your hands. IntelePeer supports webhooks which contain basic authentication built into the URL. For example: https://tomhanks:megryan@joeversusthevolcano.org

Because the URL contains the credentials, it is HIGHLY recommended (but not currently required) that the HTTPS protocol be used, which will naturally encrypt the credentials during transmission. Since IntelePeer is now in possession of those credentials, it is also recommended that any endpoint on your web server OTHER than the inbound-sms-webhook should not use the same credentials.

 

Validating that an inbound SMS originates from IntelePeer

On the other hand, if you are worried that somebody is trying to pretend to be IntelePeer and send inbound SMS messages to your webhook maliciously but you have opted not to use basic authentication (or your basic authentication credentials are compromised), don’t fret, this is where your “account secret” comes into play.

If your account secret is set, IntelePeer will supplement each inbound-sms message with a “signature”. This signature is an hmac-derivation from three things:

  • The reference ID of the SMS messages
  • The message body
  • Your account secret

The first two are part of the payload sent to your webhook. The account secret is something that only you and IntelePeer are aware of, and is NOT part of the payload by design. To calculate the expected signature, you can execute the following in your language of choice. Once you have the expected signature, you can compare it to the actual signature provided by IntelePeer and if the two match, you have validated the authenticity of the message:

Example Code

Python

import hmac, hashlib




def get_expected_signature(message_body, reference_id, account_secret):

    #the string to be signed is a concatenation of the reference ID and the body

    signing_string = u'{}{}’.format(reference_id, message_body)

    resulting_hmac = hmac.new(account_secret, signing_string, hashlib.sha1)

    return resulting_hmac.hexdigest()

 

A concrete example of using this function is below:

Example Code

Python

ACCOUNT_SECRET = 'shhhhhhhhhh!'

INBOUND_SMS = {

    u'to': u'<redacted>',

    u'message': u'This is a security test',

    u'from': u'<redacted>',

    u'refid': u'SM5ACE21340001006568000044A9F800',

    u'signature': u'67e6b7fdbed0fd11cf90de310d3bb8c0cca5650e'}




expected_signature = get_expected_signature(

                        INBOUND_SMS['message'],

                        INBOUND_SMS['refid'],

                        ACCOUNT_SECRET)

authenticated = (expected_signature == INBOUND_SMS['signature'])




print 'authenticated' if authenticated else 'not authenticated'

 

Items of note:

  • Messages are signed at the time of receipt. While changing your “account secret” is an atomic action, any messages that have already been signed but not yet delivered, will not be re-signed with the new signature prior to delivery.
  • This nifty tool is a quick validator for your code-correctness: https://www.freeformatter.com/hmac-generator.html (select SHA1)
  • If just the message body was used as the “string to be signed”, then two identical messages would produce identical signatures and with enough data points, the security of the account secret could be compromised. Thus, we concatenate the reference ID and the message body together as the “string to be signed”, which results in unique signatures, even in the case of identical messages.