埃斯林特- plugin- stripe- webhooks (西班牙语)
为 Frede Webhook 处理器执行安全和正确规则的ESLint插件.
为什么
花脸网呼克有两个出名的失败模式:
- 未验证的有效载荷. 阅读或解析请求正文之前
*.constructEvent(...)成功意味着攻击者可以向您的端点发送一个被whook形状的有效载荷,而您的操作者会向数据库发火,在数据库上写/队列任务/电子邮件. 签名检查从没有机会拒绝. - 非一能而有型盲所处理. 在瞬变故障上花边重排事件 — 一个处理器, 无需调试
event.id将重复充电,双电子邮件,双推。 一个没有分枝的操作员event.type每类事件都将运行相同的逻辑.
这六条规则定下了阻止两者的规律.
安装
pnpm add -D @boring-stack-pkg/eslint-plugin-stripe-webhooks @typescript-eslint/parser
用法(平面配置)
// eslint.config.mjs
import tsParser from "@typescript-eslint/parser";
import stripeWebhooks from "@boring-stack-pkg/eslint-plugin-stripe-webhooks";
export default [
{
files: ["**/*.{ts,tsx}"],
languageOptions: {
parser: tsParser,
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
},
plugins: { "stripe-webhooks": stripeWebhooks },
rules: stripeWebhooks.configs.recommended.rules,
},
];
建议的预设使所有六项规则都能够在 "error"通过标准的ESLint机制推翻规则.
规则
| 规则 | 级别 | 说明 |
|---|---|---|
handler-must-verify-signature | 1号线安全 | 取消阅读或转发 Webhook 有效载荷 *.constructEvent(...) 成功 . |
no-parsed-body-before-verification | 警卫 | 解析身体 API(request.json(), (中文(简体) ), JSON.parse(body), (中文(简体) ), req.body, (中文(简体) ), express.json()在核查之前. |
require-stripe-signature-header | 警卫 | 要求通过签字 constructEvent(...) ;禁止硬码 whsec_* 秘密,我们有秘密. |
handler-must-handle-event-type | 正确性 | 条纹事件处理器必须打开分支 event.type。 。 。 . |
handler-must-be-idempotent | 正确性 | 执行副作用的 Webhook 处理器必须咨询 event.id 为去杜普. |
service-must-construct-event | 公约 | 花纹感知课程 webhook- 命名方法还必须有验证方法调用 constructEvent。 。 。 . |
实例
处理器-必须核实-签名
// ❌
export async function POST(request: Request) {
const body = await request.json();
const event = stripe.webhooks.constructEvent(body, sig, k);
}
// ✅
export async function POST(request: Request) {
const body = await request.text();
const sig = request.headers.get("stripe-signature");
const event = stripe.webhooks.constructEvent(
body,
sig,
process.env.WEBHOOK_SECRET!,
);
}
处理者-必须-有能力
// ❌
export async function handle(event: Stripe.Event) {
if (event.type === "payment_intent.succeeded") {
await db.payments.insert({
/* ... */
});
}
}
// ✅
export async function handle(event: Stripe.Event) {
if (await alreadyProcessed(event.id)) return;
if (event.type === "payment_intent.succeeded") {
await db.payments.insert({
/* ... */
});
}
}
完整的规则文件及文件节选请参见 docs/rules/ 和可运行的 examples/ (Next.js App路由器,快车,Elysia/Hono,班级服务).
安全理念
这些规则是 最佳静态分析发现常见的错误——阅读 req.body 在此之前 constructEvent,硬码 whsec_* 秘密,失踪 event.type 分枝——但是他们不能证明一个网钩处理器是正确的. 具体来说,这里的任何规则本身都不足以解决问题。 与之对等的是:
- 端到端测试 条纹 CLI 网络模拟器.
- 对公关时间每个网路入口进行TIER 1安全审查.
- 集中核查(见
service-must-construct-event因此,核查步骤存在于一个地方.
静态分析的局限性
- 没有交叉函数数据流 . 一个身体参数传递到另一个文件中的帮助者没有被跟踪.
- 无有能知之类推.
Stripe.Event参数检测依赖于字形类型名称+一个字条导入在同一个文件中存在. 通用包装器类型( X)MyEvent<Stripe.Event>)不被承认. - 同上,《抽查情况报告》. 抽象去dupe逻辑的帮助函数必须指定为每个
allowedCheckFunctionPatterns. DB独有的受约束的去dupe不可见. - 中间软件核查 这条规则不承认 将看起来像"不核查"规则.
当怀疑时, 更喜欢明确, 手持 constructEvent 给一个聪明的抽象点打电话——这是规则的优化模式.
发展
pnpm install
pnpm test
pnpm typecheck
pnpm build
释放
标记 a v* 本地版本并按下标签. .github/workflows/release.yml 运行 pnpm publish --access public --no-git-checks 与 NPM_TOKEN。 。 。 .
许可证
麻省理工学院.