跳转到主要内容
Evocrawl 使用 HMAC-SHA256 对每个 Webhook 请求进行签名。验证签名可以确保请求是可信的,且未被篡改。

密钥

你的 webhook 密钥可在账户设置中的 Advanced 选项卡 找到。每个账户都有一个唯一的密钥,用于为所有 webhook 请求签名。
请妥善保管你的 webhook 密钥,切勿公开泄露。如果你认为 密钥已遭泄露,请立即在账户设置中重新生成。

签名验证

每个 webhook 请求都会包含一个名为 X-Evocrawl-Signature 的请求头:
X-Evocrawl-Signature: sha256=abc123def456...

如何验证

  1. X-Evocrawl-Signature 头中提取签名
  2. 获取原始请求体(不要先解析)
  3. 使用你的密钥计算 HMAC-SHA256
  4. 使用时间安全的比较函数比较签名

实现

import crypto from 'crypto';
import express from 'express';

const app = express();

// 使用原始请求体解析器进行签名校验
app.use('/webhook/firecrawl', express.raw({ type: 'application/json' }));

app.post('/webhook/firecrawl', (req, res) => {
  const signature = req.get('X-Evocrawl-Signature');
  const webhookSecret = process.env.FIRECRAWL_WEBHOOK_SECRET;
  
  if (!signature || !webhookSecret) {
    return res.status(401).send('未授权');
  }
  
  // 从签名头中提取哈希值
  const [algorithm, hash] = signature.split('=');
  if (algorithm !== 'sha256') {
    return res.status(401).send('签名算法无效');
  }
  
  // 计算期望的签名
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(req.body)
    .digest('hex');
  
  // 使用恒定时间比较进行签名校验
  if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(expectedSignature, 'hex'))) {
    return res.status(401).send('签名无效');
  }
  
  // 解析并处理已校验的 webhook
  const event = JSON.parse(req.body);
  console.log('已校验的 Evocrawl webhook:', event);
  
  res.status(200).send('ok');
});

app.listen(3000, () => console.log('监听端口 3000'));

最佳实践

  • 验证每个请求。 在处理 Webhook 负载之前,始终先检查签名。对于任何未通过验证的请求,返回 401 状态并予以拒绝。
  • 使用时间安全的比较。 标准字符串比较可能泄露时间信息。在 Node.js 中使用 crypto.timingSafeEqual(),在 Python 中使用 hmac.compare_digest()
  • 通过 HTTPS 提供你的端点。 这样可确保 Webhook 负载在传输过程中被加密。