Webhooks
Security Overview
Snapdocs Webhooks allow an integration to follow along with all events happening on the closings processing today for your company. Because we are posting to endpoints that are out of Snapdocs control, we have prioritized security through the following primary features. Snapdocs recommends leveraging a combination of these features be used to secure the integration, however we cannot enforce them as they are changes on the side of the integrator in many cases. Securing the integration requires a secure partner client.
| Feature | Quick Summary |
|---|---|
| No P.I.I. | Snapdocs event messages contain event names and identifiers for transactions or documents. All PII requires a follow on authenticated call. |
| IP Whitelisting | Snapdocs broadcasts from a single set of IP addresses for all of our outbound traffic to allow integrators to lock down the endpoint to our broadcast. |
| Authenticated Endpoints | Snapdocs now allows OAuth2 authenticated calls, so we can post with a Bearer token if your endpoint is a client_credentials protected endpoint |
| HMAC Signature | Snapdocs webhooks contain an HMAC key that can be validated against a signature we create, unique to every message. Only the creator and your integration can validate the signature using the HMAC key. |
| HTTPS / TLS | Snapdocs requires an Https endpoint for our webhook creation. |
Besides these features, we also recommend your integration endpoints / listener is built with appropriate rate limiting, TLS 1.3+, SSL, and as many of the above features as you can. We also encourage the regular rotation of the HMAC keys for the signature feature, by creating a new subscription to replace the old. Please rotate your HMAC keys following any security incident.
Handling of Personally Identifiable Information (PII)
Snapdocs Connect events that are broadcast will never contain PII or sensitive information. We broadcast various identifiers, statuses, and document statuses. If subsequent actions require more information or API calls, all retrieval of pertinent information requires authenticated API calls.
IP Whitelisting
To enhance security, Snapdocs supports IP whitelisting for both test and production environments. Customers may restrict access to their endpoints by specifying a list of approved IP addresses. Your Snapdocs representative can provide the current list of IP addresses used by Snapdocs services for both test and production environments. We recommend updating your firewall rules to allow only these IPs, ensuring that only authorized Snapdocs traffic can access your systems. Snapdocs recommends that your listener is built with whitelisting, rate limiting, TLS 1.3+, SSL, and the signature validation outlined below.
Authenticated Endpoints
Snapdocs Create a Subscriptionendpoint now takes in optional OAuth2 details such as token_url, client_id, client_secret for a client_credentials OAuth2 flow or Basic Auth. Our webhooks will get a token and send an authenticated call to your endpoint for all events.
Security Signature
Snapdocs Connect has a secure mechanism into our webhook broadcasts, using an HMAC hash using the secret value returned at subscription creation. Our webhooks broadcast a secure timestamp and a hash using the HMAC secret. We strongly recommend the clients validate the timestamp and signature to ensure the data is sent by Snapdocs Connect and not tampered with in transit.
The Snapdocs Connect message will include the following HTTP headers
X-Authorization-Digest, the algorithm that Snapdocs Connect uses to generate the signature, “HMACSHA256”X-Authorization-Timestamp, the ISO-8601 format of timestamp, for example “2021-12-17T19:08:59Z”X-Authorization-Signature, the base64 encoded HMAC signature to compare.
Below are steps that clients usually take to verify the signature of the payload:
- Extract the digest and timestamp from the header
- Calculate the payload signature using the digest algorithm against the timestamp, in the above example it’s HMACSHA256
- Extract the request signature from the header
- Compare the request signature with the calculated signature
- If they match, process the closing event per your integration
- If they do not match, discard the message which didn’t originate from Snapdocs
Below is an example Ruby code snippet to demonstrate the computing signature at client side:
require 'openssl'
require 'base64'
timestamp = request.headers["X-Authorization-Timestamp"]
request_signature = request.headers["X-Authorization-Signature"]
data = request.body.read
hash_bytes = OpenSSL::HMAC.digest('sha256', hmac_key, timestamp.concat(data))
computed_signature = Base64.strict_encode64(hash_bytes)
if computed_signature == request_signature
# normal process
else
# did not come from SDimport hmac
import hashlib
import base64
request_signature = request.headers["X-Authorization-Signature"]
timestamp_bytes = bytes(request.headers["X-Authorization-Timestamp"], 'utf-8')
hmac_key_bytes = bytes(hmac_key, 'utf-8')
data_bytes = request.content
hash_bytes = hmac.new(hmac_key_bytes, timestamp_bytes + data_bytes, hashlib.sha256).digest()
computed_signature = base64.b64encode(hash_bytes).decode("utf-8")
if computed_signature == request_signature:
# normal process
else:
# did not come from SDimport javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class WebhookSecurityExample {
public static void verifySignature(final String hmac_key, final String request_signature, final String timestamp, final String request_body) {
try {
final Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
final SecretKeySpec secret_key = new SecretKeySpec(hmac_key.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
final String data = timestamp + request_body;
final String encoded_signature = Base64.encodeBase64String(sha256_HMAC.doFinal(data.getBytes()));
final String computed_signature = StringUtils.newStringUsAscii(Base64.decodeBase64(encoded_signature));
if (computed_signature.equals(request_signature)) {
// normal process
} else {
// did not come from SD
} catch (Exception e) {
System.out.println("Error");
}
}
}
}using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Http;
public class SignatureVerification
{
public static void VerifyRequestSignature(HttpRequest request, string hmacKey)
{
// Read headers
string timestamp = request.Headers["X-Authorization-Timestamp"];
string requestSignature = request.Headers["X-Authorization-Signature"];
// Read request body
string requestBody;
using (var reader = new StreamReader(request.Body, Encoding.UTF8))
{
requestBody = reader.ReadToEnd();
}
// Concatenate timestamp and request body
string data = timestamp + requestBody;
// Compute HMAC-SHA256 digest
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(hmacKey)))
{
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
// Encode the hash in Base64
string computedSignature = Convert.ToBase64String(hashBytes);
if (computedSignature == requestSignature)
{
// Normal process
Console.WriteLine("Signature is valid. Proceed with the process.");
}
else
{
// Did not come from Snapdocs APIs
Console.WriteLine("Invalid signature. Request did not come from Snapdocs APIs.");
}
}
}
}Updated 11 days ago
