fix(worker): constant-time signature verification (patch)#230
Merged
liplus-lin-lay merged 1 commit intoJun 21, 2026
Merged
Conversation
Switch verifyGitHubSignature from a non-constant-time `expected === signature` hex compare to crypto.subtle.verify, which recomputes the HMAC and compares it in constant time internally. The `===` path early-exited on the first mismatched character, leaking the expected signature through response timing — a timing side-channel at the webhook trust boundary. Validate the `sha256=` prefix and a strict 64-hex-char shape before touching crypto so malformed input fails fast and uniformly, then reconstruct the raw 32-byte digest and verify it. 姉妹リポ github-rag-mcp で対応済み(#171 / #167)の定数時間化を webhook-mcp に 移植した。機能契約は不変で、正しい署名のみ true、改竄・別 secret・malformed は すべて false を返す。 Tests use GitHub's own published example vector ("Validating webhook deliveries") as an independent oracle plus a node:crypto createHmac oracle. The timing property itself is not unit-observable; the tests pin the functional contract the constant-time implementation preserves. Closes #229
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
github-webhook-mcp | 1fd39a5 | Jun 21 2026, 05:22 AM |
liplus-lin-lay
commented
Jun 21, 2026
liplus-lin-lay
left a comment
Member
Author
There was a problem hiding this comment.
AI self-review (auto mode)
実差分・テスト・CI を確認(subagent 報告の裏取り込み)。github-rag-mcp #171(#167 修正)の定数時間化を忠実に移植。
コード(signature.ts)
expected === signature(非定数時間、early-exit で期待署名が timing から漏れる)を撤去。- 署名を
sha256=prefix 検証 →^[0-9a-fA-F]{64}$(32byte)で fail-fast → 生バイト復元 →crypto.subtle.verify("HMAC", key, sigBytes, body)で内部定数時間比較。 importKeyusage を["sign]"→["verify"]に変更。- doc コメントを #227 + #229 で更新、timing side-channel の背景を明記。
- → rag-mcp
src/webhook.tsのパターンと一致。機能的契約は不変(正しい署名→true / 他→false)。
テスト(signature.test.ts)
- GitHub 公式「Validating webhook deliveries」の公開ベクトル(
secret="It's a Secret to Everybody"/body="Hello, World!"/ 既知署名)を primary oracle に。被験コードに依存しない独立検証。 - 7ケース: 公開ベクトル正常 / 本文改竄 / 別secret / prefix無し / 空 / 長さ違い / 非hex(
z×64) → 期待通り。 - 既存
node:test+node:assert/strict様式維持。
分類
- patch 妥当。入出力契約は同一、timing 特性のみ改善=ユーザー/システム観測可能な機能変化なし。
CI
- CI / test / Workers Builds 3本 pass。ローカル
npm test= tsx 54 + vitest 25 = 79本 green、tsc は baseline と同一(新規エラーなし)。
→ approve 相当。squash merge へ。
This was referenced Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #229
概要
verifyGitHubSignature(worker/src/signature.ts)の署名比較を、非定数時間のexpected === signatureからcrypto.subtle.verify(内部で HMAC を再計算し定数時間比較)に切り替えた。sha256=prefix と厳密な 64 hex 文字(32 byte)形状を crypto 到達前に検証して malformed を fail-fast し、生 32 byte digest に復元してから verify する。背景
===比較は最初の不一致文字で early-exit するため、期待署名がレスポンスタイミングから漏れる timing side-channel(webhook trust boundary の弱点)だった。姉妹リポ github-rag-mcp で対応済み(#171 / #167 修正)の定数時間化パターンを webhook-mcp に移植した。影響スコープ
patch。署名検証の内部実装+テストのみ。機能契約は不変で、入出力の観測変化なし(timing 特性のみ改善)。正しい署名→
true/ それ以外(改竄・別 secret・malformed)→false。変更ファイル
worker/src/signature.ts(実装差し替え+top コメント更新)worker/test/signature.test.ts(GitHub 公式公開ベクトルを primary oracle にケース更新)確認
npm test(worker/): tsx--test54 pass / 0 fail(うち signature 7 件)+ vitest 25 pass / 0 fail = 計 79 pass、全パス。npx tsc --noEmit: 新規型エラーなし。既存のsrc/agent.ts(31,32 行、MCP SDK 由来)2 件は baseline のまま。secret="It's a Secret to Everybody"/body="Hello, World!")を独立 oracle と一致確認済み。