USSD
Stable

Verify requests

Every request to your backend is signed. Check the signature before you trust the phone number or anything else in it.

The signature
Bash
X-Sendexa-Signature: t=1792513110,v1=5d41402abc4b2a76b9719d911017c592...
  • t is when the request was signed, in Unix seconds.
  • v1 is the HMAC-SHA256, in hex, of <t>.<raw request body> using your code's signing key.

To verify a request:

  1. Read the raw body as text, before any JSON parsing (re-encoding it changes the bytes).
  2. Compute the HMAC of t + "." + body with your key and compare it to v1 in constant time.
  3. Refuse it if t is more than 5 minutes from now, so an old request can't be replayed.
  4. On failure, return 401 and don't run your menu.

The check in your language

JavaScript
const crypto = require("crypto");
function signatureIsValid(rawBody, header, key) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
if (!key || !match) return false;
const [, t, v1] = match;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac("sha256", key).update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
// Express: keep the raw body while parsing JSON
app.use(express.json({ verify: (req, _res, buf) => (req.rawBody = buf.toString("utf8")) }));
// then: signatureIsValid(req.rawBody, req.get("x-sendexa-signature"), process.env.SENDEXA_SIGNING_KEY)

The complete servers in Sample code already do this, and include a script that sends signed test requests to your machine.