creatorpiyush/verihook
GitHub: creatorpiyush/verihook
提供统一的强类型 API,用于跨 12+ 主流服务商验证 webhook 签名,零依赖且支持多种 JS 运行时和边缘环境。
Stars: 1 | Forks: 0
# verihook 🪝
[](https://www.npmjs.com/package/verihook)
[](https://github.com/creatorpiyush/verihook/blob/main/LICENSE)
`verihook` 提供了一个统一的、强类型的 API,用于验证流行服务的 webhook 签名(**Stripe, GitHub, Shopify, Slack, Twilio, Svix/Resend/Clerk, Meta/WhatsApp, Discord, Linear, Razorpay, Zoom, Square 以及自定义 webhook**)。
无需再为每个服务寻找定制的 HMAC 代码片段,也无需为了验证传入的 webhook 而安装 10 个沉重的 SDK 依赖!
📖 阅读完整的[架构与技术规范](./ARCHITECTURE.md)。
## 功能
- ⚡ **零运行时依赖**:由标准的 Web Crypto API (`crypto.subtle`) 驱动,并带有 Node.js 回退机制。
- 🌐 **Edge 就绪**:可在任何地方运行 —— Node.js, Vercel Edge, Cloudflare Workers, Deno, Bun, Next.js, Hono, Express, Fastify。
- 🔐 **时间安全**:开箱即用地防范侧信道计时攻击。
- 🎯 **统一的类型化 API**:所有 provider 均提供简单的 `verifyWebhook(provider, req, secret)` 接口。
- ⏳ **重放攻击保护**:内置可配置的时间戳容差检查(Stripe, Slack, Svix, Zoom)。
- 🔌 **可扩展的插件系统**:可通过 `registerProvider()` 注册自定义的 provider 验证器。
## 安装
```
npm install verihook
# 或
pnpm add verihook
# 或
yarn add verihook
# 或
bun add verihook
```
## 支持的 Provider
| Provider | 标识符 | 必需的 Header / 说明 |
| :--- | :--- | :--- |
| **Stripe** | `'stripe'` | `stripe-signature` |
| **GitHub** | `'github'` | `x-hub-signature-256` 或 `x-hub-signature` |
| **Shopify** | `'shopify'` | `x-shopify-hmac-sha256` |
| **Slack** | `'slack'` | `x-slack-signature`, `x-slack-request-timestamp` |
| **Twilio** | `'twilio'` | `x-twilio-signature`(需要请求 URL;支持表单 payload 签名和 JSON `bodySHA256` 流程) |
| **Svix** | `'svix'` | `svix-id`, `svix-timestamp`, `svix-signature` |
| **Resend** | `'resend'` | 使用 Svix 签名 |
| **Clerk** | `'clerk'` | 使用 Svix 签名 |
| **WhatsApp / Meta** | `'meta'`, `'whatsapp'` | `x-hub-signature-256`(支持 `verifyMetaChallenge` GET 握手) |
| **Discord** | `'discord'` | `x-signature-ed25519`, `x-signature-timestamp`(Ed25519 签名) |
| **Linear** | `'linear'` | `linear-signature` |
| **Razorpay** | `'razorpay'` | `x-razorpay-signature` |
| **Square** | `'square'` | `x-square-hmacsha256-signature` |
| **Zoom** | `'zoom'` | `x-zm-signature`, `x-zm-request-timestamp` |
| **通用 / 自定义** | `'generic'` | 可配置的 header、算法、编码 |
## ⚡ CLI 模拟器 (`npx verihook simulate`)
在本地测试你的 webhook endpoint,**无需真实的 SaaS 账户或真实的 webhook**!CLI 会生成带有有效签名的 HMAC payload,并将其 POST 到你的服务器:
```
# 模拟 Stripe webhook
npx verihook simulate stripe --url http://localhost:3000/webhooks/stripe
# 模拟 GitHub issues 事件
npx verihook simulate github --event issues
# 模拟 WhatsApp message webhook
npx verihook simulate whatsapp --secret meta_app_secret_123
# 输出 cURL 命令而不是发送 POST
npx verihook simulate stripe --curl
```
## 快速开始
### 基本用法
```
import { verifyWebhook } from 'verihook';
const result = await verifyWebhook('stripe', req, process.env.STRIPE_WEBHOOK_SECRET!);
if (result.valid) {
console.log('Webhook verified! Timestamp:', result.timestamp);
// Parse raw body string/Buffer to access event payload data
const event = JSON.parse(req.body.toString('utf-8'));
console.log('Event Type:', event.type); // e.g. "payment_intent.succeeded"
console.log('Event Data:', event.data.object); // e.g. amount, customer ID, status
} else {
console.error(`Verification failed [${result.code}]:`, result.reason);
}
```
### 严格模式(抛出异常)
```
import { verifyWebhookOrThrow, WebhookVerificationError } from 'verihook';
try {
await verifyWebhookOrThrow('github', req, process.env.GITHUB_WEBHOOK_SECRET!);
// Process verified payload...
} catch (err) {
if (err instanceof WebhookVerificationError) {
console.error(`[${err.provider}] Verification error (${err.code}):`, err.reason);
}
}
```
### Provider 辅助函数
```
import { verifyStripe, verifyGitHub, verifySlack, verifyWhatsApp, verifyDiscord } from 'verihook';
// Provider-specific shortcut functions
await verifyStripe(req, process.env.STRIPE_SECRET!);
await verifyGitHub(req, process.env.GITHUB_SECRET!);
await verifySlack(req, process.env.SLACK_SECRET!);
await verifyWhatsApp(req, process.env.META_APP_SECRET!);
await verifyDiscord(req, process.env.DISCORD_PUBLIC_KEY!);
```
### Meta / WhatsApp 验证握手 (`verifyMetaChallenge`)
在 Meta App Dashboard 中配置 webhook 时,Meta 需要进行一次 GET 挑战握手:
```
import { verifyMetaChallenge } from 'verihook';
// In your GET /webhooks/whatsapp handler:
app.get('/webhooks/whatsapp', (req, res) => {
const result = verifyMetaChallenge(req.query, process.env.META_VERIFY_TOKEN!);
if (result.valid) {
return res.status(200).send(result.challenge);
}
return res.status(403).send(result.reason);
});
```
### 错误处理与错误代码
`verihook` 通过导出的 `WebhookErrorCode` 枚举提供结构化且类型安全的错误代码:
```
import { verifyWebhook, WebhookErrorCode } from 'verihook';
const result = await verifyWebhook('stripe', req, secret);
if (!result.valid) {
switch (result.code) {
case WebhookErrorCode.INVALID_SIGNATURE:
console.error('Signature mismatch — payload altered or secret incorrect');
break;
case WebhookErrorCode.EXPIRED_TIMESTAMP:
console.error('Timestamp outside allowed tolerance window');
break;
case WebhookErrorCode.MISSING_HEADER:
console.error('Required signature header missing');
break;
case WebhookErrorCode.INVALID_BODY:
console.error('Raw body missing — body was pre-parsed before verification');
break;
}
}
```
#### 可用的错误代码
| 错误代码 | 描述 |
| :--- | :--- |
| `WebhookErrorCode.INVALID_SIGNATURE` | HMAC 签名计算结果与传入的 header 不匹配。 |
| `WebhookErrorCode.EXPIRED_TIMESTAMP` | Webhook 时间戳超出了容差窗口(默认 300 秒)。 |
| `WebhookErrorCode.MISSING_HEADER` | 请求中缺少所需的 provider 签名 header。 |
| `WebhookErrorCode.MISSING_URL` | 缺少请求 URL(Twilio / Square 必需)。 |
| `WebhookErrorCode.INVALID_SECRET` | Webhook secret 为空或未提供。 |
| `WebhookErrorCode.INVALID_BODY` | 传入了普通的 JS 对象,但未提供 `rawBody`。 |
| `WebhookErrorCode.UNSUPPORTED_PROVIDER` | 无法识别的 provider 标识符。 |
| `WebhookErrorCode.UNKNOWN_ERROR` | 处理过程中发生意外错误(原始错误已附加到 `result.error`)。 |
## 框架集成示例
### Next.js App Router (Route Handler)
```
import { verifyWebhook } from 'verihook';
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const result = await verifyWebhook('stripe', req, process.env.STRIPE_WEBHOOK_SECRET!);
if (!result.valid) {
return NextResponse.json({ error: result.reason }, { status: 401 });
}
const payload = await req.json();
// Handle verified event...
return NextResponse.json({ received: true });
}
```
### Express.js
```
import express from 'express';
import { verifyWebhook } from 'verihook';
const app = express();
app.post('/api/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
const result = await verifyWebhook('github', req, process.env.GITHUB_SECRET!);
if (!result.valid) {
return res.status(401).json({ error: result.reason });
}
// Parse raw body string/Buffer to process verified event data
const payload = JSON.parse(req.body.toString('utf-8'));
console.log('Verified Event Action:', payload.action);
console.log('Verified Event Data:', payload.issue || payload.data?.object);
res.status(200).json({ success: true });
});
```
### Hono / Cloudflare Workers
```
import { Hono } from 'hono';
import { verifyWebhook } from 'verihook';
const app = new Hono();
app.post('/webhook', async (c) => {
const result = await verifyWebhook('shopify', c.req.raw, c.env.SHOPIFY_SECRET);
if (!result.valid) {
return c.json({ error: result.reason }, 401);
}
return c.json({ status: 'ok' });
});
export default app;
```
## 选项与自定义 Provider
### 配置选项
```
await verifyWebhook('stripe', req, secret, {
tolerance: 600, // Customize maximum allowed timestamp drift in seconds (default: 300)
now: Math.floor(Date.now() / 1000), // Override current timestamp for testing
url: 'https://example.com/api/twilio', // Override URL for Twilio / Square
});
```
### 自定义 HMAC 签名验证 (`'generic'`)
```
await verifyWebhook('generic', req, secret, {
headerName: 'x-custom-signature',
algorithm: 'sha256', // 'sha256' | 'sha1' | 'sha512'
encoding: 'hex', // 'hex' | 'base64' | 'prefix-hex'
});
```
### 注册自定义 Provider 插件
```
import { registerProvider } from 'verihook';
registerProvider({
name: 'my-service',
async verify(req, secret) {
const signature = req.headers['x-myservice-sig'];
// ... custom verification logic
return { valid: true, provider: 'my-service' };
},
});
await verifyWebhook('my-service', req, secret);
```
## License
MIT © Piyush Anand
标签:MITM代理, TypeScript, Webhook, Zenmap, 力导向图, 安全插件, 开发库, 数据完整性, 程序员工具, 签名校验, 自动化攻击