Webhook のセキュリティは、エンドポイントへのリクエストが実際に Firecrawl から送られ、改ざんされていないことを確実にするうえで極めて重要です。本ページでは、Webhook の真正性の検証方法と、セキュリティのベストプラクティスの実装について解説します。
適切な検証がないと、攻撃者は次のことが可能になります:
- 偽のWebhookリクエストを送って不要な処理を発生させる
- ペイロードを改ざんしてアプリケーションを不正に操作する
- 大量のリクエストでWebhookエンドポイントを過負荷にする
Firecrawl が Webhook に署名する方法
Firecrawl は、アカウントのシークレットキーを用いた HMAC-SHA256 により、すべての webhook リクエストに署名します。これにより、各リクエストごとに一意の署名が生成され、次のことが保証されます:
- リクエストが Firecrawl から送信されたこと
- ペイロードが改ざんされていないこと
webhook シークレットは、アカウント設定のAdvanced タブで確認できます。各アカウントには、すべての webhook リクエストに署名するために使用される固有のシークレットが割り当てられています。
webhook シークレットは厳重に管理し、決して公開しないでください。漏えいした可能性がある場合は、直ちにアカウント設定から再生成してください。
各 webhook リクエストには、以下の形式の X-Firecrawl-Signature
ヘッダーが含まれます:
X-Firecrawl-Signature: sha256=abc123def456...
署名は次の手順で算出します:
- 生のリクエストボディ(JSON文字列)を取得
- シークレットキーを用いて HMAC-SHA256 ハッシュを生成
- 16進数の文字列に変換
- 先頭に
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 で待機中'));
X-Firecrawl-Signature
ヘッダーから署名を取り出す
- 受信した生のリクエストボディをそのまま取得する(先にパースしない)
- シークレットキーと生のボディを使ってHMAC-SHA256 を計算する
- タイミングセーフな比較関数で署名を比較する
- 署名が一致した場合のみ Webhook を処理する
署名検証を行っていない webhook リクエストは決して信用しないでください。
// ❌ 悪い例 - 検証なし
app.post('/webhook', (req, res) => {
processWebhook(req.body); // 危険!
res.status(200).send('OK');
});
// ✅ 良い例 - 事前に検証
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()
- その他の言語: 「constant-time」または「timing-safe」の比較関数を探してください
Webhook には必ず HTTPS のエンドポイントを使用してください:
{
"url": "https://your-app.com/webhook" // ✅ セキュア
}
{
"url": "http://your-app.com/webhook" // ❌ 非推奨(安全でない)
}