Firecrawl はすべての Webhook リクエストに HMAC-SHA256 で署名します。署名を検証することで、リクエストが正当なものであり、改ざんされていないことを確認できます。
webhook シークレットは、アカウント設定のAdvanced タブで確認できます。各アカウントには、すべての webhook リクエストに署名するために使用される固有のシークレットが割り当てられています。
webhook シークレットは厳重に管理し、決して公開しないでください。漏えいした可能性がある場合は、直ちにアカウント設定から再生成してください。
各 webhook リクエストには、X-Firecrawl-Signature ヘッダーが含まれます:
X-Firecrawl-Signature: sha256=abc123def456...
X-Firecrawl-Signature ヘッダーから署名を取り出す
- パース前の生のリクエストボディを取得する
- シークレットキーを使って HMAC-SHA256 を計算する
- タイミングセーフな比較関数を使って署名を比較する
import crypto from 'crypto';
import express from 'express';
const app = express();
// 署名検証のために raw 本文パーサーを使用
app.use('/webhook/firecrawl', express.raw({ type: 'application/json' }));
app.post('/webhook/firecrawl', (req, res) => {
const signature = req.get('X-Firecrawl-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('検証済みの Firecrawl webhook:', event);
res.status(200).send('ok');
});
app.listen(3000, () => console.log('ポート 3000 で待機中'));
署名を検証せずに webhook を処理してはいけません。
app.post('/webhook', (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send('認可されていません');
}
processWebhook(req.body);
res.status(200).send('OK');
});
標準的な文字列比較はタイミング情報を漏えいするおそれがあります。Node.js では crypto.timingSafeEqual() を、Python では hmac.compare_digest() を使ってください。
ペイロードが通信中も暗号化されるよう、Webhook エンドポイントには必ず HTTPS を使用してください。