关于 webhook-receiver-hardener
webhook-receiver-hardener 是帮助开发者构建安全可靠 Webhook 接收端点的技能。它围绕签名校验、去重、幂等处理、快速确认与异步处理提供实现模式和运维建议。
主要功能
- 01使用 HMAC 校验签名
- 02通过 Redis 和数据库约束去重
- 03设计幂等的 Webhook 处理
- 04快速确认并异步处理请求
- 05规划错误响应、监控和事件手册
适用场景
- 01加固第三方 Webhook 接收端点
- 02防止重复投递导致重复处理
- 03按状态码处理无效请求与签名
- 04制定 Webhook 故障排查流程
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ patricio0312rev/ skills --skill webhook-receiver-hardener安装后在 Claude Code 中输入/webhook-receiver-hardener确认已加载。
详细介绍
Webhook 接收器加固
构建安全、可靠且能优雅处理故障的 Webhook 端点。
核心安全措施
签名验证:处理前进行 HMAC 验证 去重:跟踪已处理的 Webhook ID 幂等性:多次处理同一个 Webhook 也安全 重试:处理提供方的重试请求 速率限制:防止滥用
签名验证
import crypto from "crypto";
export const verifyWebhookSignature = (
payload: string,
signature: string,
secret: string
): boolean => {
const hmac = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(hmac));
};
// Stripe example
router.post(
"/webhooks/stripe",
express.raw({ type: "application/json" }),
async (req, res) => {
const sig = req.headers["stripe-signature"];
try {
const event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
await processStripeEvent(event);
res.json({ received: true });
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
}
);
去重
// Redis-based dedupe
const WEBHOOK_TTL = 60 * 60 * 24; // 24 hours
export const isDuplicate = async (webhookId: string): Promise<boolean> => {
const key = `webhook:${webhookId}`;
const exists = await redis.exists(key);
if (exists) return true;
await redis.setex(key, WEBHOOK_TTL, "1");
return false;
};
// Usage
if (await isDuplicate(webhook.id)) {
return res.status(200).json({ received: true }); // Already processed
}
幂等处理
export const processWebhook = async (webhook: Webhook) => {
// Use database transaction with unique constraint
try {
await db.transaction(async (trx) => {
// Insert webhook record (unique constraint on webhook_id)
await trx("processed_webhooks").insert({
webhook_id: webhook.id,
processed_at: new Date(),
});
// Do actual processing
await performWebhookAction(webhook, trx);
});
} catch (err) {
if (err.code === "23505") {
// Unique violation
console.log("Webhook already processed");
return; // Idempotent - already processed
}
throw err;
}
};
重试处理
// Acknowledge immediately, process async
router.post("/webhooks/provider", async (req, res) => {
// Verify signature
if (!verifySignature(req.body, req.headers["signature"])) {
return res.status(401).send("Invalid signature");
}
// Return 200 immediately
res.status(200).json({ received: true });
// Process async
processWebhookAsync(req.body).catch((err) => {
console.error("Webhook processing failed:", err);
// Will be retried by provider
});
});
// Exponential backoff for provider retries
// Attempt 1: immediate
// Attempt 2: +5 minutes
// Attempt 3: +15 minutes
// Attempt 4: +1 hour
// Attempt 5: +6 hours
错误响应
// Return appropriate status codes
const webhookHandler = async (req, res) => {
// 400: Malformed payload (won't retry)
if (!isValidPayload(req.body)) {
return res.status(400).json({ error: "Invalid payload" });
}
// 401: Invalid signature (won't retry)
if (!verifySignature(req.body, req.headers["signature"])) {
return res.status(401).json({ error: "Invalid signature" });
}
// 200: Already processed (idempotent)
if (await isDuplicate(req.body.id)) {
return res.status(200).json({ received: true });
}
// 500: Processing error (will retry)
try {
await processWebhook(req.body);
return res.status(200).json({ received: true });
} catch (err) {
console.error("Processing error:", err);
return res.status(500).json({ error: "Processing failed" });
}
};
监控与运行手册
## Webhook Incidents
### High Error Rate
1. Check provider status page
2. Review recent code deploys
3. Check signature secret rotation
4. Verify database connectivity
### Missing Webhooks
1. Check provider sending (their dashboard)
2. Verify endpoint is accessible
3. Check rate limiting rules
4. Review dedupe cache TTL
### Duplicate Processing
1. Check dedupe cache connectivity
2. Verify unique constraints
3. Review idempotency logic
最佳实践
- 在任何处理之前验证签名
- 尽快返回 200,异步处理
- 使用 Redis 和数据库约束进行去重
- 记录所有 Webhook 尝试
- 监控处理延迟
- 为故障设置警报
- 记录预期的负载数据结构
输出检查清单
- 签名验证
- 去重机制
- 幂等处理
- 异步处理模式
- 正确的状态码
- 错误日志记录
- 监控/警报
- 事件运行手册
评论
支持 Markdown