目的
verifyGitHubSignature(worker/src/signature.ts)の署名比較が expected === signature の非定数時間比較で、最初の不一致文字で early-exit する=期待署名がレスポンスタイミングから漏れるタイミングサイドチャネル(trust boundary の弱点)。姉妹リポ github-rag-mcp で対応済み(#171 / #167 修正)の定数時間化を webhook-mcp にも移植する。
方針(rag-mcp #171 と同一パターン)
- hex 文字列の
=== 比較をやめる。sha256= prefix 検証 → ^[0-9a-fA-F]{64}$(32byte)を事前検証して malformed を fail-fast → 生バイト(Uint8Array(32))に復元 → crypto.subtle.verify("HMAC", key, sigBytes, body) で内部定数時間比較。
importKey の usage を ["sign"] → ["verify"] に。
- 機能的契約は不変: 正しい署名→
true / それ以外(改竄・別secret・malformed)→false。
テスト
- 既存
signature.test.ts を rag-mcp webhook.test.ts のケースに揃える。GitHub 公式ドキュメント「Validating webhook deliveries」の公開ベクトル(secret="It's a Secret to Everybody" / body="Hello, World!" / 既知署名)を独立 oracle に。
- 非hex ケース(
sha256= + "z".repeat(64) → false)を追加し、新しい hex 検証を pin。
- harness は webhook-mcp 既存様式(
node:test + node:assert/strict、tsx --test)を維持。rag-mcp の vitest 構文は移植しない。
影響スコープ
- 署名検証の内部実装+テストのみ。機能的観測変化なし(timing 特性のみ改善、入出力契約は同一)→ patch。
target files
worker/src/signature.ts(実装差し替え)
worker/test/signature.test.ts(ケース更新)
関連
目的
verifyGitHubSignature(worker/src/signature.ts)の署名比較がexpected === signatureの非定数時間比較で、最初の不一致文字で early-exit する=期待署名がレスポンスタイミングから漏れるタイミングサイドチャネル(trust boundary の弱点)。姉妹リポ github-rag-mcp で対応済み(#171 / #167 修正)の定数時間化を webhook-mcp にも移植する。方針(rag-mcp #171 と同一パターン)
===比較をやめる。sha256=prefix 検証 →^[0-9a-fA-F]{64}$(32byte)を事前検証して malformed を fail-fast → 生バイト(Uint8Array(32))に復元 →crypto.subtle.verify("HMAC", key, sigBytes, body)で内部定数時間比較。importKeyの usage を["sign"]→["verify"]に。true/ それ以外(改竄・別secret・malformed)→false。テスト
signature.test.tsを rag-mcpwebhook.test.tsのケースに揃える。GitHub 公式ドキュメント「Validating webhook deliveries」の公開ベクトル(secret="It's a Secret to Everybody"/body="Hello, World!"/ 既知署名)を独立 oracle に。sha256=+"z".repeat(64)→ false)を追加し、新しい hex 検証を pin。node:test+node:assert/strict、tsx --test)を維持。rag-mcp の vitest 構文は移植しない。影響スコープ
target files
worker/src/signature.ts(実装差し替え)worker/test/signature.test.ts(ケース更新)関連
signature.ts抽出)