Webhook 認証 - エンドポイントを保護する
finlight は、webhook の配信を保護するために複数の認証方法を提供します。各 webhook には既定で署名検証が含まれ、セキュリティを強化するための追加認証レイヤーをオプションで利用できます。
finlight は、単独または組み合わせて使用できる 4 つの認証方法をサポートします:
なし
既定の署名検証以外に追加の認証はありません。
使用するタイミング:
- テストおよび開発環境
- 安全なネットワーク内部のエンドポイント
- 署名検証で十分なセキュリティが得られる場合
注: この設定に関係なく、署名検証はすべての webhook リクエストに引き続き含まれます。
X-Finlight-Key ヘッダー
各 webhook リクエストで、カスタムの X-Finlight-Key ヘッダーに API キーを送信します。
構成:
- webhook の設定時に API キーを指定します
- そのキーは
X-Finlight-Keyヘッダーに含まれます
実装: エンドポイントは、受信したヘッダーを検証する必要があります:
const finlightKey = req.headers['x-finlight-key']
if (finlightKey !== 'your-expected-api-key') {
return res.status(401).send('Invalid API key')
}
送信されるヘッダー:
X-Finlight-Key: your-api-key-value
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
ベーシック認証
ユーザー名/パスワードの資格情報による HTTP ベーシック認証。
構成:
- webhook の設定時にユーザー名とパスワードを設定します
- 資格情報は base64 エンコードされ、
Authorizationヘッダーで送信されます
実装: エンドポイントは標準の HTTP ベーシック認証を受け取ります:
const auth = req.headers.authorization
if (!auth || !auth.startsWith('Basic ')) {
return res.status(401).send('Missing Basic Auth')
}
const credentials = Buffer.from(auth.slice(6), 'base64').toString()
const [username, password] = credentials.split(':')
if (username !== 'expected-user' || password !== 'expected-pass') {
return res.status(401).send('Invalid credentials')
}
送信されるヘッダー:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
X-Webhook-Signature: sha256=signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
署名検証
自動セキュリティ: 選択した認証方法に関係なく、すべての webhook リクエストに署名検証が含まれます。
仕組み:
- finlight は webhook 送信時にタイムスタンプを生成します
- 次のように連結してメッセージを作成します:
timestamp + '.' + payload - webhook のシークレットキーを使い、HMAC-SHA256 でメッセージに署名します
- 署名とタイムスタンプの両方をヘッダーで送信します
含まれるヘッダー:
X-Webhook-Signature: sha256=computed-signature
X-Webhook-Timestamp: 2024-01-15T10:30:00.000Z
署名アルゴリズム:
message = timestamp + '.' + raw_request_body
signature = HMAC-SHA256(message, webhook_secret)
X-Webhook-Timestamp ヘッダーが存在しない場合、メッセージは生のボディのみになります。結果は一定時間で比較し、タイムスタンプが 5 分より古い配信は拒否してください。
ボディは、解析されていない生のリクエストバイトである必要があります。 読み取る前に JSON をデコードして再エンコードするミドルウェアはバイト列を変えてしまい、署名を無効にします — これは検証失敗の最も一般的な原因です。ボディを生の文字列またはバッファーとして読み取り、検証してから解析してください。
公式クライアントでの検証
すべてのクライアントライブラリには、チェック全体を代行するヘルパーが用意されています:署名の sha256= プレフィックスを受け入れ、一定時間で比較し、5 分のリプレイウィンドウを強制し、解析済みの記事を返します。検証に失敗した場合は例外をスローするかエラーを返します — 必ず 4xx で応答し、ペイロードは決して処理しないでください。
webhook を検証する
import express from 'express'
import { WebhookService } from 'finlight-client'
const app = express()
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
try {
const article = WebhookService.constructEvent(
req.body.toString(),
req.headers['x-webhook-signature'] as string,
process.env.WEBHOOK_SECRET!,
req.headers['x-webhook-timestamp'] as string,
)
console.log('New article:', article.title)
res.sendStatus(200)
} catch (err) {
console.error('Webhook verification failed:', err)
res.sendStatus(400)
}
})
セキュリティのベストプラクティス
タイムスタンプの検証
上記の constructEvent ヘルパーはすでに 5 分のリプレイウィンドウを強制しているため、これが必要なのは署名を手動で検証する場合のみです:
function isTimestampValid(timestamp, toleranceSeconds = 300) {
const now = Date.now()
const requestTime = new Date(timestamp).getTime()
const difference = Math.abs(now - requestTime) / 1000
return difference <= toleranceSeconds
}
資格情報の安全な保管
- 環境変数: すべてのシークレットを環境変数に保管します
- シークレット管理: AWS Secrets Manager、HashiCorp Vault などを使用します
- ハードコードしない: シークレットをバージョン管理にコミットしないでください
- 定期的なローテーション: webhook のシークレットを定期的に更新します
webhook 設定のガイダンスについては、webhook のメインドキュメントを参照してください。包括的なテストについては、webhook テストガイドを確認してください。