How to Secure Fastio Webhooks with Signature Verification
Fastio webhook security and signature verification ensures incoming requests come from Fastio and protects your endpoints from malicious payloads. Webhook signatures use HMAC-SHA256 on the raw payload with your secret, often including a timestamp to block replays. This guide walks through setup, verification code in Node.js and Python, replay protection, idempotency, and production tips for agentic workflows.
Why Implement Fastio Webhook Security?
Webhooks deliver real-time notifications for file uploads, modifications, and access in Fastio workspaces. Without security, attackers can spoof events, send tampered payloads, or replay old events to trigger unwanted actions.
Signature verification confirms the sender and payload integrity. Fastio signs payloads with HMAC-SHA256 using your secret key. A timestamp header prevents replays beyond a short window, like 5 minutes.
Without verification, endpoints remain open to abuse. Signature verification blocks spoofing and ensures only legitimate Fastio events are processed. This is important for agentic teams handling sensitive files.
Set Up Your Webhook Signing Secret
Log into your Fastio dashboard at fast.io. Navigate to Organization Settings > Webhooks. Generate a signing secret for your endpoint URL.
Copy the secret securely. Store it in environment variables, never in code. Rotate secrets periodically via the dashboard.
Configure your webhook URL in Fastio. Select events like file.uploaded, file.modified, file.accessed. Test with the dashboard's "Send Test Event" to verify delivery.
Verify Webhook Signatures in Code
Extract the signature from X-Fastio-Signature: sha256=hexdigest. Use raw request body for signing. Compute HMAC-SHA256(secret, timestamp + "." + rawBody) and compare timing-safely.
Node.js Example:
const crypto = require('crypto');
function verifySignature(req, secret) {
const sig = req.headers['x-fastio-signature']?.split('=')[1];
const ts = req.headers['x-timestamp'];
const rawBody = JSON.stringify(req.body); // or Buffer.from(req.rawBody)
if (!sig || !ts) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${ts}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
}
app.post('/webhook', (req, res) => {
if (!verifySignature(req, process.env.FASTIO_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process event
res.status(200).send('OK');
});
Python Example (FastAPI):
import hmac
import hashlib
from fastapi import Request, HTTPException
async def verify_signature(request: Request, secret: str):
sig = request.headers.get('x-fastio-signature', '').split('=')[1]
ts = request.headers.get('x-timestamp')
raw_body = await request.body()
if not sig or not ts:
raise HTTPException(401, "Missing headers")
expected = hmac.new(
secret.encode(), f"{ts}.{raw_body.decode()}".encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
raise HTTPException(401, "Invalid signature")
@app.post("/webhook")
async def webhook(request: Request):
await verify_signature(request, os.getenv("FASTIO_SECRET"))
# Process
return {"status": "ok"}
Always use the raw body before middleware parses it. Test with curl requests that mimic the headers.
Build Secure Agentic File Workflows
Secure your Fastio webhooks today. Get 50GB free storage, 5,000 credits/month for agents. No credit card needed.
Prevent Replay Attacks with Timestamp Checks
Replay attacks reuse old valid events. Fastio includes an X-Timestamp header (Unix milliseconds). Reject events older than 300,000 ms (5 minutes).
Add to verification:
Node.js:
const now = Date.now();
if (now - parseInt(ts) > 300000) return false;
Python:
import time
if time.time() * 1000 - int(ts) > 300000:
raise HTTPException(401, "Replay detected")
Combine with idempotency checks for full protection.
Add Idempotency for Duplicate Handling
Webhooks retry on failure. Use unique event_id from payload. Store processed IDs in Redis/DB with TTL.
Node.js with Redis:
const redis = require('redis');
const client = redis.createClient();
if (await client.get(`webhook:${event_id}`)) {
return res.status(200).send('Duplicate');
}
await client.setex(`webhook:${event_id}`, 3600, 'processed');
// Process event
On startup, query your DB for recent events. Use workspace_id:event_id for multi-tenant setups.
Production Best Practices and Troubleshooting
- Enforce HTTPS only.
- Use queues (BullMQ, Celery) for processing.
- Rate limit by IP.
- Log raw payloads/signatures for audits.
- Monitor 4xx for sig fails.
Troubleshooting:
Integrate with MCP for agent workflows: trigger on file events.
For agentic teams, secure webhooks enable reactive automation without polling.
Frequently Asked Questions
How do I verify a Fastio webhook?
Extract X-Fastio-Signature and X-Timestamp. Compute HMAC-SHA256(secret, timestamp + '.' + rawBody). Compare timing-safely. Return 401 on fail, 200 on success.
What algorithm does Fastio use for webhook signatures?
HMAC-SHA256 on timestamp.payload with your secret. Header is sha256=hexdigest. Use crypto libraries for constant-time comparison.
How to prevent Fastio webhook replay attacks?
Check X-Timestamp < 5 min old. Reject stale events. Add idempotency with event_id dedup for retries.
Where to get the webhook signing secret?
Fastio dashboard > Organization Settings > Webhooks. Generate per endpoint. Rotate regularly.
What if signature verification fails?
Log the raw body and headers. Common issues: non-raw body, wrong secret, clock skew. Test with dashboard test events.
Related Resources
Build Secure Agentic File Workflows
Secure your Fastio webhooks today. Get 50GB free storage, 5,000 credits/month for agents. No credit card needed.