Verifying requests
So that you can verify a webhook or data connector request came from ProsperStack, each request includes a signature header.
The ProsperStack-Signature header contains a timestamp and a signature. You
can extract these values to verify that the request originated from
ProsperStack.
An example signature looks like:
t=1660874139,s=05ba90dc69f562b66a79dc28f40cacff6210388c804ece5094c80c4d8a89af88
Verifying the signature
1. Extract the timestamp and signature from the header
Split the header using the , character as the separator to get a list of
elements. Then split each element using the = character as the separator to
get a prefix and value pair.
The value associated with the t prefix corresponds to the timestamp, and the
value associated with the s prefix corresponds to the signature.
2. Prepare the signature payload string
Create the signature payload string by concatenating:
- The timestamp (as a string)
- The
.(dot) character - The request body (i.e. the JSON-stringified request payload)
3. Compute the expected signature
Compute an HMAC with the SHA256 hash function using the prepared signature payload string from the previous step as the message and your ProsperStack client secret as the key.
To access your client secret, click the gear icon in the main navigation. In the General tab's Account card, click Reveal client secret. Keep this value on your server and do not expose it in browser code.
4. Compare the signatures
Compare the signature value from the ProsperStack-Signature header and your
computed signature from the previous step to make sure they match. To protect
against timing attacks, make sure to use a constant-time string comparison
function when comparing the signature values.
To prevent replay attacks, compare the timestamp from the
ProsperStack-Signature header and the current timestamp to make sure the
difference is within your tolerance.
Verification example
Verifying the signature will look different depending on your server language, but the following is an example of what it might look like in Node.js:
import crypto from "crypto";
import { differenceInSeconds } from "date-fns";
const SECRET = "my client secret";
const TOLERANCE_SECONDS = 300;
const body = req.body;
const signatureHeader = req.headers["prosperstack-signature"];
const signatureValues = signatureHeader
.split(",")
.map((part) => part.split("="))
.reduce(
(acc, [key, value]) => ({
...acc,
[key]: value,
}),
{}
);
const { t: timestamp, s: signature } = signatureValues;
if (
differenceInSeconds(new Date(), new Date(Number(timestamp) * 1000)) >
TOLERANCE_SECONDS
) {
throw new Error("Timestamp is out of tolerance!");
}
const computedSignature = crypto
.createHmac("sha256", SECRET)
.update(timestamp + "." + body)
.digest("hex");
if (
computedSignature.length !== signature.length ||
!crypto.timingSafeEqual(
Buffer.from(computedSignature),
Buffer.from(signature)
)
) {
throw new Error("Signatures do not match!");
}