fix(rust-mcp-session): compare stored token_digest against presented token#2180
Merged
imran-siddique merged 1 commit intoMay 12, 2026
Conversation
…token
`McpSessionAuthenticator` records `sha256_hex(token)` in `McpSession`
.token_digest` at issue time but never compares it during `authenticate`,
so the captured digest is dead state — authentication accepts any token
that passes HMAC verification and matches the payload's session id /
agent id / expiry, even when the stored session record carries a digest
for a different token.
Add a constant-time check in `authenticate`: after the agent / expiry /
metadata checks, compute `sha256_hex(token)` and compare it to
`session.token_digest`. Failure returns `AccessDenied { reason: "session
token digest mismatch" }`. A local `constant_time_eq` helper (XOR
accumulation; early-return only on length mismatch since session digests
are fixed-length 64-char hex) keeps the failure path timing-independent
without taking on a `subtle` direct dependency.
Applied identically in both `agentmesh/src/mcp/session.rs` and
`agentmesh-mcp/src/mcp/session.rs` — the two crates publish byte-identical
copies of the MCP subtree.
Tests:
- `rejects_session_with_mismatched_token_digest` — issue a session, tamper
the store-side digest to all-zeros, assert authenticate() refuses.
- `constant_time_eq_matches_regular_eq` — covers empty inputs, equal
inputs, single-byte difference, and length mismatch in both orders.
🤖 AI Agent: security-scanner — Security ReviewSecurity Review
|
🤖 AI Agent: breaking-change-detector — API CompatibilityAPI Compatibility
|
🤖 AI Agent: docs-sync-checker — Docs SyncDocs Sync
|
🤖 AI Agent: code-reviewer — View detailsTL;DR: 1 blocker, 0 warnings. The digest comparison in the authentication process is missing, which could lead to security issues.
Add a comparison of the computed token digest against the stored digest in the |
🤖 AI Agent: test-generator — `agent-governance-rust/agentmesh/src/mcp/session.rs`
|
|
🟡 Contributor Check: MEDIUM
Automated check by AGT Contributor Check. |
PR Review Summary
Verdict: ❌ Changes needed |
MohammadHaroonAbuomar
pushed a commit
to MohammadHaroonAbuomar/agt-acs
that referenced
this pull request
Jun 1, 2026
…token (microsoft#2180) `McpSessionAuthenticator` records `sha256_hex(token)` in `McpSession` .token_digest` at issue time but never compares it during `authenticate`, so the captured digest is dead state — authentication accepts any token that passes HMAC verification and matches the payload's session id / agent id / expiry, even when the stored session record carries a digest for a different token. Add a constant-time check in `authenticate`: after the agent / expiry / metadata checks, compute `sha256_hex(token)` and compare it to `session.token_digest`. Failure returns `AccessDenied { reason: "session token digest mismatch" }`. A local `constant_time_eq` helper (XOR accumulation; early-return only on length mismatch since session digests are fixed-length 64-char hex) keeps the failure path timing-independent without taking on a `subtle` direct dependency. Applied identically in both `agentmesh/src/mcp/session.rs` and `agentmesh-mcp/src/mcp/session.rs` — the two crates publish byte-identical copies of the MCP subtree. Tests: - `rejects_session_with_mismatched_token_digest` — issue a session, tamper the store-side digest to all-zeros, assert authenticate() refuses. - `constant_time_eq_matches_regular_eq` — covers empty inputs, equal inputs, single-byte difference, and length mismatch in both orders.
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.
Summary
agent-governance-rust/agentmesh/src/mcp/session.rsand its byte-identical copy inagent-governance-rust/agentmesh-mcp/src/mcp/session.rs.McpSessionAuthenticatorrecordssha256_hex(token)inMcpSession.token_digestat issue time but never compares it duringauthenticate, so the captured digest is dead state. Authentication today accepts any token that passes HMAC verification and matches the payload's session id / agent id / expiry — even if the stored session record carries a digest for a different token (e.g. a tampered store, or a re-issued token reusing the same session id).The HMAC signature is the primary authenticator, so today's exploitability is low (an attacker has to forge HMAC first), but the digest is plainly intended as a defence-in-depth binding between issued token and persisted session. Leaving it uncompared turns a documented invariant into wishful thinking.
Change
In
authenticate(), after the existing agent/expiry/metadata checks, recomputesha256_hex(token)and compare it tosession.token_digestvia a localconstant_time_eqhelper (XOR accumulation; early-return only on length mismatch since session digests are fixed-length 64-char hex). On mismatch, returnAccessDenied { reason: "session token digest mismatch" }. No external dependency added —subtlewould be a heavier hammer than this single comparison needs.Applied identically in both copies of
mcp/session.rssince the two crates publish byte-identical copies of the MCP subtree.Tests
rejects_session_with_mismatched_token_digest— issue a session, overwrite the store-side digest with all-zeros, assertauthenticate()refuses with the new reason.constant_time_eq_matches_regular_eq— covers empty inputs, equal inputs, single-byte difference, and length mismatch in both orders.cargo test --workspace --lib mcp::session— 8 passed (4 per crate), 0 failed.Test plan
issues_and_authenticates_sessionsandenforces_concurrent_limitstill pass — happy-path authentication is unaffectedSurfaced during independent audit conducted by @finnoybu (Ken Tannenbaum, AEGIS Initiative); [LOW, Rust].