Webhook Security
Verify webhook signatures to ensure payloads are from Pipes.bot.
When you configure a webhook secret, Pipes.bot signs every payload with an HMAC-SHA256 signature. You should verify this signature on your server to ensure the request actually came from Pipes.bot.
Signature header
Pipes.bot includes the signature in the X-Pipes-Signature header as a hex-encoded HMAC-SHA256 digest of the raw request body.
X-Pipes-Signature: a1b2c3d4e5f6...Verifying the signature
Compute the HMAC-SHA256 digest of the raw request body using your webhook secret, then compare it to the header value using a timing-safe comparison.
Node.js / Express
import crypto from "crypto";
function verifySignature(rawBody: string | Buffer, signature: string, secret: string): boolean {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
}
// Express middleware
app.post("/webhook", express.text({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-pipes-signature"] as string;
if (!signature || !verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const payload = JSON.parse(req.body);
// Process verified payload...
res.sendStatus(200);
});Python / Flask
import hmac
import hashlib
def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.route("/webhook", methods=["POST"])
def webhook():
signature = request.headers.get("X-Pipes-Signature", "")
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
return "Invalid signature", 401
payload = request.get_json()
# Process verified payload...
return "", 200Best practices
- Always verify signatures in production. Without verification, anyone who discovers your endpoint URL can send fake payloads.
- Use a strong secret. Generate one with the dashboard button or use at least 32 random hex characters.
- Use timing-safe comparison. Standard string equality (
===,==) is vulnerable to timing attacks. Usecrypto.timingSafeEqual(Node.js) orhmac.compare_digest(Python). - Parse after verifying. Verify the signature against the raw request body before parsing JSON to avoid discrepancies from re-serialization.