10

We are looking at setting up our Salesforce org as a public and secure endpoint target for a 3rd party application (Housecall Pro) which will be sending us webhook calls. The only rub is they cannot first authenticate to our Salesforce org, so we need some way to make our Salesforce target public and secure.

I found an article online which describes making a REST service public in Salesforce. I'm not sure if there are any other ways to do this and if this is the best way to do it.

Assuming I can establish a public REST endpoint, our 3rd party application said they can authenticate their webhook call using the following method, and I quote:

We include 2 values in the header of the webhook. You can use these to authenticate that the webhook came from us and not a malicious source.

Api-Signature: HMAC hex digest of the timestamp and request body joined by '.' respectively, generated using the SHA-256 hash function and the partners secret as the HMAC key Api-Timestamp: Timestamp of the instant the event fired

Are there any patterns or best practices to setup Salesforce as a public endpoint for webhooks that I should consider besides the approach I laid out above? If no patterns or best practices exist, are there any security concerns with this approach?

Thank you.

3
  • out of curiosity, what is the 3rd party application ?
    – glls
    Jul 8, 2021 at 17:40
  • 1
    I updated the question to include the 3rd party app (Housecall Pro). Jul 8, 2021 at 17:46
  • 2
    seems reasonable to me (SFDC knows the partner secret and can recompute the HMAC to verify it matches. It is exactly the way I built my Shopify webhook responder - based on this approach
    – cropredy
    Jul 8, 2021 at 22:14

1 Answer 1

1

Everything you listed using the REST resource annotation and the request context looks good, and you can still secure it. Here are some tips specifically for HouseCall Pro webhooks:

  1. Store all webhook requests - this will help debug during processing.

    // use textarea fields
    new WithoutSharing().doInsert(new HouseCall_Webhook__c(
        Data__c = RestContext.request.requestBody.toString(),
        Headers__c = Json.serialize(RestContext.request.headers)
    ));
    
  2. Save any records in System Mode instead of granting CRUD/FLS to Site Guest User.

    This lets your Apex logic do its DML, without granting any permissions to the site:

    without sharing class WithoutSharing
    {
        Database.SaveResult doInsert(SObject record)
        {
            return Database.insert(record);
        }
    }
    
  3. Verify each request signature - HouseCall uses the standard HMAC with SHA256: https://docs.housecallpro.com/docs/housecall-public-api/46e9e1be07621-webhooks

    Salesforce provides Crypto.verifyHMAC constant-time comparison function for this exact purpose. Use the protected fields of a Custom Metadata Type to hold the signing secret. This will keep your customer's information security team happy:

    // Retrieve secret from custom metadata
    RestRequest request = RestContext.request;
    String signingSecret = HouseCall__mdt.getInstance('Main').Secret__c;
    Blob key = EncodingUtil.base64Decode(signingSecret);
    
    // Prepare signature body eg "1706571019.{json}"
    String timestamp = request.headers.get('Api-Timestamp');
    String concatBody = timestamp + '.' + request.requestBody.toString();
    Blob concatBlob = Blob.valueOf(concatBody);
    
    // Verify their signature vs our signature
    String signature = request.headers.get('Api-Signature');
    Blob hmac = EncodingUtil.base64Decode(signature);
    Boolean result = Crypto.verifyHmac('hmacSHA256', concatBlob, key, hmac);
    
    // Throw exception if it does not match
    System.assert(result, 'HouseCall Pro signature mismatch');
    
  4. Use an IP restricted Site - set Login IP Ranges on the Site Guest User profile. HouseCall should publish their outbound request origins like this - or ask support for their IP ranges.

    site-profile-ip-restrictions

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .